替代c ++中的条件编译,使用多个.cpp文件

时间:2017-11-21 00:55:40

标签: c++ conditional-compilation

假设我有一个使用条件编译的C ++类:

C.hpp

namespace NS{
  class C {
    public:
      C(void);
      ~C(void);
      int func( int arg1 );
    private:
      int memberVar;
}

C.cpp:

#include "C.hpp"

namespace NS{
  C::C( void ){ memberVar = 0; }
  C::~C( void ) {}
  int C::func( int arg1 ){
    int retval = 0;
    memberVar = arg1;
#ifdef DEV_BUILD
    retval = memberVar;
    printf( "memberVar was set.\n" );
#endif
    return retval;
  }
}

(这是一个简化的例子;假设类C有几百行,其中一些函数使用条件编译,每个函数使用相同的条件:#ifdef DEV_BUILD。该应用程序是嵌入式的 - 系统应用程序,条件用于区分测试硬件和生产硬件,它们有一些差异)

我被建议改为在单独的.cpp文件中实现func ...但我有点想知道最好的方法。

我的第一个冲动是创建C_dev.cpp和C_prod.cpp,在每个中实现C::func()不同,然后编辑C.cpp:

#include "C.hpp"
#ifdef DEV_BUILD
#include "C_dev.cpp"
#else
#include "C_prod.cpp"
#endif

namespace NS{
  C::C( void ){ memberVar = 0; }
  C::~C( void ) {}
}

......但是这种不好的风格?或者其他问题?

其他限制:

  • 不能使用子类。
  • 无法使用模板类。

(更新:建议使用单独的文件,但不是必需的。我愿意接受其他建议)

2 个答案:

答案 0 :(得分:4)

重点1:如果您在其他相同代码的海洋中分散了数百个小的变化,请使用条件编译。它通常更容易阅读和维护。

但是如果在功能级别上存在差异,则可以在不同的实现文件之间拆分不同的功能。通常我用操作系统包装器来做这件事,比如在Linux和Windows上获取目录列表的差异非常粗糙。 (注意:这个例子已经被C ++淘汰了)

大点数2:不要制作多个标题。这只会使问题复杂化。您希望在两个实现之间公开一个公共接口。如果你做不到,你已经迷失了,需要找到一条不同的道路。制作适合所有实现的通用标头。

对于这个答案,所有实现都使用Asker的原始标题C.hpp:

namespace NS{
  class C {
    public:
      C(void);
      ~C(void);
      int func( int arg1 );
    private:
      int memberVar;
}

我将Asker的例子分成三个cpp文件:一个在所有构建中具有共同功能,另外两个实现包含差异的功能。这样你就不会重复任何你不一定需要重复的功能。

C_common.cpp所有共享功能都在这里

#include "C.hpp"

namespace NS{
  C::C( void ){ memberVar = 0; }
  C::~C( void ) {}
}

C_debug.cpp:带有调试语句的函数在这里

#include "C.hpp"

namespace NS{
  int C::func( int arg1 ){
    int retval = 0;
    memberVar = arg1;
    retval = memberVar;
    printf( "memberVar was set.\n" );
    return retval;
  }
}

C_no_debug.cpp:没有调试语句的函数到这里

#include "C.hpp"

namespace NS{
  int C::func( int arg1 ){
    memberVar = arg1;
    return memberVar;
  }
}

链接程序时,始终链接C_common并指定要链接的C_debug和C_no_debug中的哪一个。

有时,您可以通过分解复杂函数并仅隔离差异并将差异赋予共享函数内调用的函数来更进一步。

C_common.cpp

#include "C.hpp"

namespace NS{
  C::C( void ){ memberVar = 0; }
  C::~C( void ) {}
  int C::func( int arg1 ){
    memberVar = arg1;
    debugstuff(); // calls the different functionality
    return memberVar;
  }
}

C_debug.cpp:

#include "C.hpp"

namespace NS{
    void debugstuff()
    {
        printf( "memberVar was set.\n" );
    }
}

C_no_debug.cpp:

#include "C.hpp"

namespace NS{
    void debugstuff()
    {
    }
}

这可能会很难缩放,因为你可能会遇到许多衬里功能。请参阅上面的大点1。但如果差异形成恰到好处,您可以利用传递函数参数来减少垃圾邮件。在这种情况下,逻辑上要做的是传入调试字符串。一个打印,另一个丢弃。

C_common.cpp

#include "C.hpp"

namespace NS{
  C::C( void ){ memberVar = 0; }
  C::~C( void ) {}
  int C::func( int arg1 ){
    memberVar = arg1;
    debugstuff("memberVar was set.\n"); // calls the different functionality
                                        // and tells it what to do!
    return memberVar;
  }
}

C_debug.cpp:

#include "C.hpp"

namespace NS{
    void debugstuff(const char * message)
    {
        printf( message );
    }
}

C_no_debug.cpp:

#include "C.hpp"

namespace NS{
    void debugstuff(const char * )
    {
    }
}

答案 1 :(得分:0)

如果没有更多信息,我建议无论谁告诉你使用不同的文件,都会引导你走向艰难的方向。

让我谈谈具体问题:...but is this bad style? Or otherwise problematic?

是和是。

  • 与其他人所做的相反,哪种暗示你做错了。此外,任何新的开发人员都必须学习系统独特的“构建怪癖”。
  • 这意味着您的构建系统/软件存储库有两倍的文件可以跟踪。
  • 你发现的任何错误都必须在两个文件中修复(这就是我保持文件同步的意思)。
  • 您可能需要一种方法来了解使用哪个版本的源来构建exe。