如何验证函数不会自行调用?

时间:2017-06-13 13:43:54

标签: c++ linux c++11 gcc crtp

这是我的代码,在这里你看到类型为Base的对象,接口函数为Do(),我总是使用Base类型的对象(用更多类型模板化)并始终调用{ {1}}界面。因此,对于Do()ImplBaseDummy1ImplBaseDummy2的{​​{1}}的具体实现或f()的{​​{1}}的默认实现。到目前为止,所有这些都很有效,我有很多代码可以按预期工作,我不想更改API,但如果你能说服我,我可能会这样做。最近我不得不添加更多类型,如ImplBaseDummy2我尝试永远不会重复代码,所以我创建了一个类似的结构,希望它将继续工作。问题是如何在定义时强制调用Base的特定实现,并在缺少特定实现时使用默认实现(来自ImplBaseDummy1),所有这些都应该在调用接口函数ImplBase时发生对于f类型的对象(以某种方式模板化)。

Base

此代码按预期编译和运行。我的问题是关于我想要实现的安全机制,以防忘记在Do()中写Base(这是初始意图,如果缺少特定实现,请调用{{1}的默认实现}})。在这种情况下会发生的是#include <iostream> template <class Impl> class Base { public: void Do() { f_impl(); } void f() {std::cout << "Base::f" << std::endl; } protected: void f_impl() { static_cast<Impl*>(this)->f(); } }; class ImplBaseDummy1 : public Base<ImplBaseDummy1> { }; class ImplBaseDummy2 : public Base<ImplBaseDummy2> { public: void f() {std::cout << "ImplBaseDummy2::f" << std::endl; } }; template <class Actual> class ImplBase : public Base<ImplBase<Actual>> { public: typedef Base<ImplBase<Actual>> Parent; void f() { static_cast<Actual*>(this)->f(); } }; class Derived1 : public ImplBase<Derived1> { public: typedef ImplBase<Derived1> Parent; void f() {std::cout << "Derived1::f" << std::endl; } }; class Derived2 : public ImplBase<Derived2> { public: typedef ImplBase<Derived2> Parent; using Parent::Parent::f; }; int main() { Base<ImplBaseDummy1> d01; d01.Do(); Base<ImplBaseDummy2> d02; d02.Do(); Base<ImplBase<Derived1>> d1; d1.Do(); Base<ImplBase<Derived2>> d2; d2.Do(); return 0; } 无限期地调用自身直到它崩溃(或在编译using Parent::Parent::f;优化级别时永不退出)。我想实现它类似于

Derived2

但两个实现都没有编译,任何其他想法如何实现此检查?要强调的是,运行时解决方案不是我想要的,我在任何检查时都会更好,当我遇到问题而不是执行额外的测试来执行对{{{{ 1}} interface。

3 个答案:

答案 0 :(得分:2)

一般情况下,无法在编译时证明重新进入。

但可以在运行时检查:

static bool inside = false;
assert(!inside);
inside = true;

// rest of function

inside = false;
return whatever; // if non void

局部变量的析构函数在技术上仍然可以最终调用该函数。这可以通过(可重用的)RAII样式对象部分解决:

class nonreentrant
{
    bool& inside;
    public:
    nonreentrant(bool& inside): inside(inside)
    {
        assert(!this->inside);
        this->inside = true;
    }
    ~nonreentrant()
    {
        this->inside = false;
    }
};

// usage
static bool inside = false;
nonreentrant guard(inside);

// rest of function

不幸的是,这不适用于参数,其析构函数将在之后运行。

从技术上讲,仍然可能有一些移动构造函数涉及返回值可能最终调用该函数。无效功能和返回简单可移动物体的功能应该是万无一失的。

答案 1 :(得分:1)

您的代码执行未定义的行为,因此无法检查它是否正常工作&#34;。该标准对程序的行为没有任何要求;所有行为都是&#34;正确&#34;。

ServletRequestDataBinder binder = new ServletRequestDataBinder(formBean); binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true, 10)); binder.registerCustomEditor(Boolean.class, new PropertyEditorSupport() { @Override public void setAsText(String text) { logger.debug("boolean text : " + text); // setValue(type); } }); binder.registerCustomEditor(String[].class, new PropertyEditorSupport() { @Override public void setAsText(String text) { logger.debug("array text : " + text); // setValue(type); } }); 不是ImplBase<Derived2>,因此Derived2会导致未定义的行为结果。这里的无限循环和你想要发生的一样正确。

我已经解决了类似的问题而没有做出未定义的行为。

static_cast<Derived2*>(this)->f()

现在,template <class Impl> class Base { public: void f_impl() {std::cout << "Base::f" << std::endl; } }; template <class Actual> class ImplBase : public Base<ImplBase<Actual>> { public: void f() { static_cast<Actual*>(this)->f_impl(); } }; class Derived1 : public ImplBase<Derived1> { public: void f_impl() {std::cout << "Derived1::f" << std::endl; } }; class Derived2 : public ImplBase<Derived2> { public: }; Derived1 d1; d1.f();做了我认为你想要的事情; Derived2 d2; d2.f();已覆盖d1,而f使用d2行为。

我们从实现中拆分接口,允许实现调度独立于接口调度而存在。

答案 2 :(得分:1)

不要使用Parent :: Parent :: f声明。 delcare一个f()显式调用parent :: Parent :: f()。那样可以解决你的问题。

如果您想要额外保护以防止忘记声明,您应该声明具有受保护继承的最终类,如:

class Derived1 : protected ImplBase<Derived1>
{
public:
 typedef ImplBase<Derived1> Parent;
 void f() {std::cout << "Derived1::f" << std::endl; }
};

class Derived2 : protected ImplBase<Derived2>
{
public:
 typedef ImplBase<Derived2> Parent;
 // void f() { Parent::Parent::f(); }  // uncomment to avoid compile error.
};

生成的错误消息非常清楚,并按名称提及错误的类。

这段代码非常人为。我很难搞清楚这种继承有用的目的,因为只需直接声明Derived1或Derived2对象类型的对象就可以很好地工作,而不需要原始代码中的ImplBase&lt;&gt; :: f()。

这与元编程有关吗?我们非常感兴趣。

这是完整的解决方案,改变了4行代码。

#include <iostream>

template <class Impl>
class Base
{
public:
    void Do() { f_impl(); }
    void f() { std::cout << "Base::f" << std::endl; }
protected:
    void f_impl() { static_cast<Impl*>(this)->f(); }
};

class ImplBaseDummy1 : public Base<ImplBaseDummy1>
{
};

class ImplBaseDummy2 : public Base<ImplBaseDummy2>
{
public:
    static void f() { std::cout << "ImplBaseDummy2::f" << std::endl; }
};

template <class Actual>
class ImplBase : public Base<ImplBase<Actual>>
{
public:
    typedef Base<ImplBase<Actual>> Parent;
    void f() { static_cast<Actual*>(this)->f(); }
};

class Derived1 : protected ImplBase<Derived1>
{
public:
    typedef ImplBase<Derived1> Parent;
    void f() { std::cout << "Derived1::f" << std::endl; }
};

class Derived2 : protected ImplBase<Derived2>
{
public:
    typedef ImplBase<Derived2> Parent;
    //using Parent::Parent::f;
    //void f() { Parent::Parent::f(); } // uncomment to remove compile error
};

int main()
{
    Base<ImplBaseDummy1> d01;
    d01.Do();
    Base<ImplBaseDummy2> d02;
    d02.Do();
    Base<ImplBase<Derived1>> d1;
    d1.Do();
    Base<ImplBase<Derived2>> d2;
    d2.Do();
    return 0;
}