dynamic_cast <>模板的变量参数

Pau*_*ulH 4 c++ templates variadic-functions

我有一个执行测试用例的C++应用程序.某些测试用例可能依赖于其他测试用例的输出.

所有测试用例都实现了基本接口:

/// base class for all test cases
class ITest
{
public:
    virtual void Execute() = 0;
};
Run Code Online (Sandbox Code Playgroud)

产生一些可能对其他测试用例有用的对象的测试用例实现了这个接口:

/// implemented by test cases that provide data to other test cases
template< class Obj >
class IDependency
{
public:
    virtual Obj Get() = 0;
};
Run Code Online (Sandbox Code Playgroud)

需要来自其他测试用例的数据的测试用例实现此接口:

/// implemented by test cases that require data from other test cases
template< class Obj >
class IDependent
{
public:

    void SetDependency( IDependency< Obj >* dependency )
    {
        dependency_ = dependency;
    };

protected:
    Obj GetDependency() const
    {
        return dependency_->Get();
    };

private:
    IDependency< Obj >* dependency_;
};
Run Code Online (Sandbox Code Playgroud)

两个示例测试用例.一个人需要一个const wchar_t物体; 一个产生该对象:

/// A test case that provides a "const wchar_t*" object to other test cases
class Foo : public ITest, 
            public IDependency< const wchar_t* >
{
public:
    const wchar_t* Get() 
    { 
        if( object_.length() == 0 )
            Execute();
        return object_.c_str();
    };

    virtual void Execute()
    {
        printf( "Execute Foo\n" );
        object_ = L"Object produced by Foo";
    };

private:
    std::wstring object_;
};

/// A test case that first requires a "const wchar_t*" object
class Bar : public ITest,
            public IDependent< const wchar_t* >
{
public:

    virtual void Execute()
    {
        const wchar_t* needed_object = GetDependency();

        printf( "Execute Bar with %S\n", needed_object );
    };
};
Run Code Online (Sandbox Code Playgroud)

测试用例存储在列表中.通过注册过程将案例添加到列表中:

/// List of test cases to execute
std::vector< ITest* > list_;

/// Register a test case to execute with the system
void Register( ITest* test_case )
{
    list_.push_back( test_case );
}
Run Code Online (Sandbox Code Playgroud)

这是我的问题.我想实现'Register()'函数的重载,该函数也接受依赖项.但是,因为依赖关系可以是任何类型(不仅仅是这个例子中的'const wchar_t*'),我不知道如何管理它.以下是我正在寻找的或多或少的示例,但我不确定如何使其工作.

/// Register a test case with dependencies with the system
void Register( ITest* test_case, ITest* dependency, ... )
{
    IDependent< ??? >* dependent = dynamic_cast< IDependent< ??? >* >( test_case );
    IDependency< ??? >* dep = dynamic_cast< IDependency< ??? >* >( dependency );

    va_list dep_list;
    for( va_start( dep_list, dependency ); 
         NULL != dep; 
         dep = dynamic_cast< IDependency< ??? >* >( va_arg( dep_list, ITest* ) ) )
    {
        dependent->SetDependency( dep );
    }
    va_end( dep_list );

    Register( test_case );
}
Run Code Online (Sandbox Code Playgroud)

示例用法:

int _tmain( int argc, _TCHAR* argv[] )
{
    /// Test case Foo
    Foo foo;

    /// Test case bar (depends on Foo)
    Bar bar;

    /// Register test case Bar with a dependency on Foo
    Register( &bar, &foo );

    /// Execute Bar. Because it depends on Foo, that will be executed first
    list_->begin()->Execute();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

预期产量:

Execute Foo
Execute Bar with Object produced by Foo
Run Code Online (Sandbox Code Playgroud)

有没有人对如何成功实现这种架构有任何建议?(或者更好的架构实际上有效吗?)

谢谢,PaulH

Pau*_*oke 7

我看到两种可能的解决方案

静态的

使Register()方法成为模板.简单的解决方案是将依赖项的数量限制为某个合理的最大值.

template <class T, class D1>
void Register(T* test_case, IDependency<D1>* d1)
{
    BOOST_STATIC_ASSERT(boost::is_base_and_derived<IDependent<D1>, T>::value);
    // since we now know that T is a IDependent<D1>, a dynamic_cast would only be necessary
    // to allow virtual inheritance.
    static_cast<IDependent<D1>*>(test_case)->SetDependency(d1);
    Register(test_case);
}

template <class T, class D1, class D2>
void Register(T* test_case, IDependency<D1>* d1, IDependency<D2>* d2)
{
    BOOST_STATIC_ASSERT(boost::is_base_and_derived<IDependent<D1>, T>::value);
    static_cast<IDependent<D1>*>(test_case)->SetDependency(d1);
    Register(test_case, d2);
}

template <class T, class D1, class D2, class D3>
void Register(T* test_case, IDependency<D1>* d1, IDependency<D2>* d2, IDependency<D3>* d3)
{
    BOOST_STATIC_ASSERT(boost::is_base_and_derived<IDependent<D1>, T>::value);
    static_cast<IDependent<D1>*>(test_case)->SetDependency(d1);
    Register(test_case, d2, d3);
}

// ...
Run Code Online (Sandbox Code Playgroud)

对于支持可变参数模板的编译器,这可能只能在一个函数模板中编写,用于无限量的依赖项.

或者你可以让Register()返回一个代理类,这样你就可以这样写:

Register(test_case)(dep1)(dep2)(dep3) /* ... */ (depN);
Run Code Online (Sandbox Code Playgroud)

代理类将存储指向容器和测试用例的指针,并定义一个函数调用操作符,它看起来就像上面示例中的Register(T*test_case,IDependency*d1)函数,只是没有"test_case" "参数和对Register(test_case)的最终调用(可以在代理类的dtor中执行).

动态

如果我理解你正在尝试做什么,每个"依赖"只能产生一种结果.在这种情况下,您可以像这样修改IDependency接口:

class IDependencyBase
{
public:
    virtual void ApplyTo(ITest* target) = 0;
};

template <class T>
class IDependency : public IDependencyBase
{
public:
    virtual void ApplyTo(ITest* target)
    {
        // cast to reference gives us an std::bad_cast if the types are not compatible,
        // which I think is a good thing here
        dynamic_cast<IDependancy<T>&>(*target).SetDependancy(this); 
    }

    virtual T Get() = 0;
};

template <class InputIterator>
void Register(ITest* test_case, InputIterator begin, InputIterator end)
{
    for (; begin != end; ++begin)
    {
        IDependancyBase* dep = *begin;
        dep->ApplyTo(test_case);
    }
    Register(test_case);
}

template <class Container>
void Register(ITest* test_case, Container deps)
{
    Register(test_case, deps.begin(), deps.end());
}
Run Code Online (Sandbox Code Playgroud)

现在看来你再次实现你的varargs解决方案似乎很诱人,就像这样(从第二个例子开始):

void Register(ITest* test_case, ...)
{
    va_list dep_list;
    va_start(dep_list, test_case);
    while(IDependencyBase* dep = va_arg(dep_list, ITest*))
        dep->ApplyTo(test_case);

    va_end(dep_list);

    Register( test_case );
}

// and use it like

int _tmain( int argc, _TCHAR* argv[] )
{
    Foo foo;
    Bar bar;

    Register(&foo);
    Register(&bar, &foo, 0);

    list_->begin()->Execute();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,这并不能保证有效.在上面的代码中,Foo*存储为varagrs参数,并作为IDependencyBase*读回.这不能保证工作,因为Foo和IDependencyBase都不是POD(IIRC它们都必须是POD才能保证工作 - 也许它不能保证,即使那样,我也必须在标准中查找) .这不是一个遥不可及的"不是由站立但不会在任何地方工作"的事情.引入多重和/或虚拟继承,这几乎可以保证失败.

使用C++时的一般建议:除非没有其他方法,否则不要使用varargs函数.总有另一种方式.