I am designing a toolkit that has several modules. I'm trying to make the modules as independent as possible, so they can even be compiled independently (e.g. as a library).
One of the modules is logging
and another one is geometry
. Right now a base class in geometry
receives a pointer to a logging
object and then uses it to log data:
#include "../logging/logger.h"
class GeometryBase {
public:
//...
void do_something() { if (logger) logger->debug("doing something"); }
void setLogger(Logger* logger) {//...};
private:
Logger* logger = nullptr;
};
So for this I need to include ../logging/logger.h
, which means compiling this module requires logging
headers. Is there a way to get around this, so even if the logging
headers don't exist, this would still compile?
Right now I can think of using macros to make all the parts related to logging conditional during preprocessing. Like:
#ifdef USE_LOGGING
#include "../logging/logger.h"
#endif
class GerometryBase {
//...
void do_something() { if (logger) _log("doing something"); }
#ifdef USE_LOGGING
void _log(const std::string& s) {//...}
Logger* logger = nullptr;
#else
void _log(const std::string& s) {// do nothing}
void* logger = nullptr;
#endif
}; // class
Are there better/cleaner ways to do this? Are there recommended guidelines or best practices for such design?
==================================================================================
Update
Here is an example implementation using function pointers (based on rioki's idea) that does help decoupling the objects:
obj.h
#ifndef MYOBJ_H_
#define MYOBJ_H_
#include <iostream>
class MyObj {
public:
MyObj() { std::cout << "constructing MyObj" << std::endl; }
void setLogger( void (*p)(const char*, int) ) {
logger = p;
}
void do_somthing() {
if (logger) {
logger("this is a debug message", 1);
}
}
private:
void (*logger)(const char*, int ) = nullptr;
};
#endif
logger.h
#ifndef LOGGER_H
#define LOGGER_H
void logger(const char* , int);
#endif
logger.cpp
#include <iostream>
#include "logger.h"
void logger(const char* str, int lvl) {
std::cout << "level " << lvl << " " << str << std::endl;
}
main.cpp
#include "logger.h"
#include "obj.h"
int main() {
MyObj obj;
obj.setLogger(logger);
obj.do_somthing();
return 0;
}
output:
constructing MyObj
level 1 this is a debug message