Skip to content

File Debug.h

File List > Debug > Debug.h

Go to the documentation of this file

#pragma once




// TODO: this file has the downside, that it defines ostream operators and formatToVec for specific types. This means, that files that define these
// types cannot include this file, due to circular dependencies. In order to improve this, the ostream operators and formatToVec functions should be
// moved to the files that define these types.

#include <array>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

// This include is necessary, as Debug implements a special formatting for Ray.
#include "Rays.h"
#include "Shader/Ray.h"

// Debug only code; use it as: DEBUG(<statement>);
#ifdef RAYX_DEBUG_MODE
#define RAYX_DEBUG(x) (x)
#else
#define RAYX_DEBUG(x) \
    do {              \
    } while (0)
#endif

#define STRING(s) #s

namespace rayx {

// OSTREAM CONVERSION

inline std::ostream& operator<<(std::ostream& os, const complex::Complex& c) { return os << "{" << c.real() << ", " << c.imag() << "}"; }

template <typename T>
inline std::ostream& operator<<(std::ostream& os, const glm::tvec2<T>& v) {
    return os << "{" << v.x << ", " << v.y << "}";
}

template <typename T>
inline std::ostream& operator<<(std::ostream& os, const glm::tvec3<T>& v) {
    return os << "{" << v.x << ", " << v.y << ", " << v.z << "}";
}

template <typename T>
inline std::ostream& operator<<(std::ostream& os, const glm::tvec4<T>& v) {
    return os << "{" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << "}";
}

// LOGGING SYSTEM

// activates / deactivates the printing of RAYX_VERB "verbose" prints.
void RAYX_API setDebugVerbose(bool);

// reads the "verbose" flag.
bool RAYX_API getDebugVerbose();


// The implementation of RAYX_LOG
struct RAYX_API Log {
    Log(std::string filename, int line);
    ~Log();

    template <typename T>
    Log& operator<<(T t) {
        std::cout << t;
        return *this;
    }
};

// The implementation of RAYX_WARN
struct RAYX_API Warn {
    Warn(std::string filename, int line);

    ~Warn();

    template <typename T>
    Warn& operator<<(T t) {
        std::cerr << t;
        return *this;
    }
};

// The implementation of RAYX_EXIT
struct RAYX_API Exit {
    std::string filename;
    int line;

    Exit(const std::string& filename, int line);

    ~Exit();

    template <typename T>
    Exit& operator<<(T t) {
        std::cerr << t;
        return *this;
    }
};

// The implementation of RAYX_VERB
struct RAYX_API Verb {
    Verb(std::string filename, int line);
    ~Verb();

    template <typename T>
    Verb& operator<<(T t) {
        if (getDebugVerbose()) { std::cout << t; }
        return *this;
    }
};

// An empty implementation used in release when using "debug-only" prints like RAYX_D_LOG.
struct RAYX_API IgnoreLog{template <typename T> IgnoreLog & operator<<(T){return *this;
}  // namespace rayx
}
;

// the function to be called after RAYX_EXIT happens.
// normally exit(1), but in the test suite it's ADD_FAILURE.
extern void RAYX_API (*error_fn)();

// Defines the actual RAYX logging macros using the structs defined above.
// The __FILE__ and __LINE__ macros contain the current filename and linenumber.
// They are defined for us by c++ https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
#define RAYX_LOG  rayx::Log(__FILE__, __LINE__)
#define RAYX_WARN rayx::Warn(__FILE__, __LINE__)
#define RAYX_EXIT rayx::Exit(__FILE__, __LINE__)
#define RAYX_VERB rayx::Verb(__FILE__, __LINE__)

#ifdef RAYX_DEBUG_MODE
// In debug mode, RAYX_D_LOG is just the same as RAYX_LOG.
#define RAYX_D_LOG  RAYX_LOG
#define RAYX_D_WARN RAYX_WARN
#define RAYX_D_ERR  RAYX_EXIT
#define RAYX_D_VERB RAYX_VERB

#else
// In release mode, RAYX_D_LOG instead calls the IgnoreLog, hence discarding the print.
#define RAYX_D_LOG  rayx::IgnoreLog()
#define RAYX_D_WARN rayx::IgnoreLog()
#define RAYX_D_ERR  rayx::IgnoreLog()
#define RAYX_D_VERB rayx::IgnoreLog()
#endif

// COLLECTION DEBUGGING SYSTEM

/*
 *
 * In the following we define
 * RAYX_DBG: prints collection to RAYX_LOG for debugging
 *
 * example usage:
 * RAYX_DBG(orientation);
 * RAYX_DBG(position);
 * */

// catch any unimplemented usage of formatAsVec
template <typename T>
inline std::vector<double> formatAsVec(T) {
    // abort compilation and print type T
    [[maybe_unused]] typedef typename T::something_made_up X;
    return {};
}

inline std::vector<double> formatAsVec(int arg) { return {static_cast<double>(arg)}; }
inline std::vector<double> formatAsVec(RandCounter arg) { return {static_cast<double>(arg)}; }
inline std::vector<double> formatAsVec(EventType arg) { return {static_cast<double>(arg)}; }
inline std::vector<double> formatAsVec(double arg) { return {arg}; }
inline std::vector<double> formatAsVec(complex::Complex arg) { return {arg.real(), arg.imag()}; }

template <int N, int M, typename T>
inline std::vector<double> formatAsVec(const glm::mat<N, M, T> arg) {
    std::vector<double> out;
    for (size_t i = 0; i < N * M; i++) {
        auto data = formatAsVec(arg[i / N][i % N]);
        out.insert(out.end(), data.begin(), data.end());
    }
    return out;
}

template <int N, typename T>
inline std::vector<double> formatAsVec(const glm::vec<N, T> arg) {
    std::vector<double> out;
    for (size_t i = 0; i < N; i++) {
        auto data = formatAsVec(arg[i]);
        out.insert(out.end(), data.begin(), data.end());
    }
    return out;
}

template <size_t N, typename T>
inline std::vector<double> formatAsVec(const std::array<T, N> arg) {
    std::vector<double> out;
    for (size_t i = 0; i < N; i++) {
        auto data = formatAsVec(arg[i]);
        out.insert(out.end(), data.begin(), data.end());
    }
    return out;
}

template <typename T>
inline std::vector<double> formatAsVec(const std::vector<T> arg) {
    std::vector<double> out;
    for (size_t i = 0; i < arg.size(); i++) {
        auto data = formatAsVec(arg[i]);
        out.insert(out.end(), data.begin(), data.end());
    }
    return out;
}

template <>
inline std::vector<double> formatAsVec<double>(const std::vector<double> arg) {
    return arg;
}

inline std::vector<double> formatAsVec(const Rays& rays) {
    std::vector<double> out;
    auto insert = [&out](const std::vector<double>& v) { out.insert(out.end(), v.begin(), v.end()); };

#define X(type, name, flag) insert(formatAsVec(rays.name));

    RAYX_X_MACRO_RAY_ATTR
#undef X

    return out;
}

void dbg(const std::string& filename, int line, std::string name, std::vector<double> v);

#define RAYX_DBG(C) rayx::dbg(__FILE__, __LINE__, #C, rayx::formatAsVec(C))

}  // namespace RAYX