模块化C++设计

use*_*208 3 c++ modular

我正在设计一个包含多个模块的工具包.我正在尝试使模块尽可能独立,因此它们甚至可以独立编译(例如作为库).

其中一个模块是logging另一个模块geometry.现在,基类geometry接收指向logging对象的指针,然后使用它来记录数据:

#include "../logging/logger.h"
class GeometryBase {
    public:
      //...
      void do_something() { if (logger) logger->debug("doing something"); }
      void setLogger(Logger* logger) {//...};
    private:
      Logger* logger = nullptr;
};
Run Code Online (Sandbox Code Playgroud)

所以为此,我需要包含../logging/logger.h,这意味着编译此模块需要logging标题.有没有办法绕过这个,所以即使logging标题不存在,这仍然可以编译?

现在我可以考虑使用宏来在预处理期间使所有与记录条件相关的部分相关.喜欢:

#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
Run Code Online (Sandbox Code Playgroud)

是否有更好/更清洁的方法来做到这一点?是否有针对此类设计的建议指南或最佳实践?

================================================== ================================

更新

下面是一个使用函数指针(基于rioki的想法)的示例实现,它有助于解耦对象:

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 
Run Code Online (Sandbox Code Playgroud)

logger.h

#ifndef LOGGER_H
#define LOGGER_H

void logger(const char* , int);

#endif
Run Code Online (Sandbox Code Playgroud)

logger.cpp

#include <iostream>
#include "logger.h"

void logger(const char* str, int lvl) {

    std::cout << "level " << lvl << " " << str << std::endl;

}
Run Code Online (Sandbox Code Playgroud)

main.cpp中

#include "logger.h"
#include "obj.h"

int main() {
    MyObj obj;

    obj.setLogger(logger);
    obj.do_somthing();


    return 0;

}
Run Code Online (Sandbox Code Playgroud)

输出:

constructing MyObj
level 1 this is a debug message
Run Code Online (Sandbox Code Playgroud)

Che*_*Alf 5

对于"所以它们甚至可以独立编译",你可以将类声明为一个类,

class Logger;
Run Code Online (Sandbox Code Playgroud)

然后你可以随意使用它来形成正式的结果和参数类型,但由于编译器不知道它的大小或它的成员你不能用它做任何事情,比如在函数实现中.

但是在另一个标题中包含标题并将其包含在实现文件中之间存在很大差异:后者对总构建时间贡献一次,而前者可能贡献很多次,每个翻译单元一次.

另一方面,如果你在做只有标题的模块,那么就没有办法包括所有相关的代码.