派生类使用模板没有开销?

时间:2013-05-17 17:38:43

标签: c++ templates inheritance optimization macros

我正在努力完成以下任务:

  1. 对象。
  2. 在函数中调试具有额外功能的对象版本以进行跟踪。
  3. 现在,我目前有一个使用宏的编译时解决方案,如果未使用正确的标志编译库,则解析为do {} while(0)

    我想将此功能转移到在运行时启用。这样做的最佳方式是什么?

    我想做:Base * obj = (isGlobalDebugEnabled) ? new Debug(...) : new Base(...);类型的事情。我是不是想要这样的东西?

    请注意,标准虚函数并不能真正解决问题,因为每个函数都必须在对象的派生(调试)版本中重复,从而无法实现目的。

    此外,最低级别的功能是非常高的音量(配置文件时大约6000亿次调用)所以我想为“基类”编译一个编译的零开销解决方案。当然,Debug对象可能更慢。

    这就是我想到模板的原因。 注意:除了VS2010功能(基本lambda等)之外,我没有C ++ 11 / boost访问权限。我可以做点什么吗

    template <bool debug = false>
    class Object {
        std::enable_if<debug> void printTrace(); // Add functions based on debug/not
    };
    void Object::doSomething(...){
        <only do this if debug without runtime check> addToTrace(...);
        doTheStuff();
    }
    

    我看到this link指出了虚假继承与模板方向,如果有帮助的话。

    感谢您的帮助

    AK

    编辑:我刚刚意识到我可能会采用错误的方式 - 也许将Debug对象作为基类,并在Regular对象中使用no-ops覆盖功能。这似乎是一种更好的方式。但是,由于这些高性能要求,我仍然希望避免vtable跳转,所以我猜我的模板问题仍然存在?可能?

    EDIT2:正如KerrickSB所指出的,使用的一个例子可能更清楚:

    主要的exe代码:

    void ComputeSomething() {
        Object * obj = (globalDebugFlag) ? new DebugObject(...) : new Object(...);
        obj->insertElement(elem); // Inserts in Object, Inserts and traces actions in DebugObject
        ...
    }
    

    其中Object当前是一个单独的DLL,其中globalDebugFlag是一个(建议的)全局变量,由一个命令设置,该命令通过一个单独的端口而不是导致调用ComputeSomething()的端口。

    我计划使用全局跟踪变量,然后将跟踪推回端口(通过处理此端口的全局对象),以便在前端工具上显示。

2 个答案:

答案 0 :(得分:3)

根据定义,运行时决策意味着您在运行时的所有成本(而不是编译时间)做出决策。你不会从那里得到解决。

但是,您可以将检查推送到调用堆栈,直到它们不经常发生以满足您的需求为止。当然,更改调试标志的效果会延迟一点(多少取决于您忽略的检查)。使用模板,您可以复制/专门化代码以进行调试和非调试版本,而无需复制源代码。

template <bool debug>
class Object {
  void something() {
    // branch on compile-time constant - can be optimized
    if (!debug) return;
    // ...
  }
}

template<bool debug>
useObject(Object<debug> o) {
    for(int i = 0; i < 10000; ++i) {
        // statically calls specialized implementation
        o.something();
    }
}

debugEnabled ? useObject(Object<true>()) : useObject(Object<false>());

答案 1 :(得分:1)

这是一个非常基本的想法。我不确定它是否会概括或扩展,但我们可以讨论。

static bool debug_mode = /* ... */;    // global

class Container
{
    struct ContainerImpl
    {
        virtual ~ContainerImpl() { }
        virtual void insert(int) = 0;
        std::unique_ptr<ContainerImpl> clone() const = 0;
    };

    std::unique_ptr<ContainerImpl> impl;
public:
    Container()
    : impl(debug_mode ? new DebugImpl : new MainImpl)
    { }

    Container(Container const & rhs)
    : impl(rhs.impl->clone())
    { }

    Container(Container && rhs) noexcept
    : impl(std::move(rhs.impl))
    { }

    // also implement assignment


    /*** Main interface ***/

    void insert(int x)
    {
        impl->insert(x);
    }


    /*** Implementations ***/

    struct MainImpl : ContainerImpl { /* main implementation */ };

    struct DebugImpl : MainImpl  // just for example
    {
        virtual void insert(int x)
        {
            // trace insertion
            MainImpl::insert(x);
        }

        std::unique_ptr<ContainerImpl> clone() const
        {
            return { new DebugImpl(*this); }
        }
    };
};

现在你可以使用Container作为普通的值类型对象,它将在内部使用不同的实现,具体取决于标志。