【问题标题】:Using template metaprogramming to wrap C-style variadic arguments使用模板元编程来包装 C 风格的可变参数
【发布时间】:2010-12-10 16:13:02
【问题描述】:

我有一个 Visual Studio 2008 C++ DLL,它导出一个接受可变参数的函数,如下所示:

__declspec( dllexport ) void DLL_Foo( int count, ... )
{
    va_list list;
    va_start( list, count );

    for( ; count; --count )
    {
        const wchar_t* item = va_arg( list, const wchar_t* );
        if( count % 2 == 0 )
            printfW( L"[%s] ", item );
        else
            printfW( L"%s ", item );
    }

    va_end( list );

    printfW( L"\r\n" );
}

预期的用法是这样的:

DLL_Foo( 4, L"int", L"1", L"const wchar_t*", L"Hello" );

输出的位置:

[int] 1, [const wchar_t*] Hello

为了简化这个函数的使用,我打算包含一个像这样的 C++ 模板函数:

template< class T1, class T2 >
void Foo( T1 p1, T2 p2 )
{
    std::wstringstream t1W;
    t1W << typeid( p1 ).name();
    std::wstringstream p1W;
    p1W << p1;

    std::wstringstream t2W;
    t2W << typeid( p2 ).name();
    std::wstringstream p2W;
    p2W << p2;

    ::DLL_Foo( 4, t1W.str().c_str(), p1W.str().c_str(), t2W.str().c_str(), p2W.str().c_str() );
};

预期的用法是这样的:

int a = 1;
const wchar_t* b = L"Hello";
Foo( a, b );

具有与以前相同的预期输出。

有没有我可以使用的模板递归方法,这样我就不必为 0..n 参数实现不同的 template&lt;&gt; Foo() 函数?

template<> void Foo();
template< class T1 > void Foo( T1 p1 );
template< class T1, ..., class N > void Foo( T1 p1, ..., N n );

请不要解决涉及可变参数模板或其他 C++0x 功能的解决方案。我意识到它们很棒,但我使用的是 VS2008。另外,为了更难,我不能使用 boost::MPL 之类的增强功能。

谢谢, 保罗H


编辑: 是的,DLL 实际的 dll 函数不仅仅打印类型和值信息。实际的 DLL 函数看起来更像这样:

__declspec( dllexport ) void DLL_Foo( MYHANDLE handle, int count, ... )
{
    CMyObject* obj = reinterpret_cast< CMyObject* >( handle );

    va_list list;
    for( va_start( list, count ); count; --count )
    {
        const wchar_t* item = va_arg( list, const wchar_t* );
        if( count % 2 == 0 )
        {
            obj->AddTypeInfo( item );
        }
        else
        {
            obj->AddValueInfo( item );
        }
    }

    va_end( list );
}

【问题讨论】:

  • 如果那是函数的来源,为什么不直接将其丢弃并自己将函数放入 Foo() 中呢?一半的函数只是变量参数处理。
  • @DeadMG - 这不是完整的功能。 DLL_Foo() 将处理信息并用于更改作为句柄传入的对象的状态,然后将其作为句柄返回。如果这与您的建议不一致,您能否详细说明您的意思?

标签: c++ templates recursion metaprogramming


【解决方案1】:

这只能使用 C++0x 功能。如果你不能使用它的可变参数模板,你就不能创建一个函数……呃,它需要可变数量的模板。

另一方面,您可以为同一方法创建多个重载,每个重载都有自己的参数数量(1……某个上限)。这当然是相当多的工作。

【讨论】:

  • 不完全。如果他可以访问 Boost.PP 和 Boost.MPL,他也可以做​​到。从技术上讲,您可以看看 Boost.MPL 如何实现 vector 并编写自己的代码。您基本上必须自己重新实现部分 MPL 和 PP。
  • 它不必是真正的可变参数。固定的上限解决方案是可以接受的。 0..6个参数,比如应该没问题。
【解决方案2】:

康拉德的回答是对的。

但是,您可以通过让Foo 接受一个元组来避免编写多个重载,但会给您的用户带来一点不便,例如:

template<class TupleT>
void Foo(const TupleT& Args)

并要求调用者在调用 Foo 时将参数包装在一个元组中:

//Foo(an_int, a_bool, a_whatever);
Foo(boost::make_tuple(an_int, a_bool, a_whatever));

...现在,就在我即将单​​击“发布您的答案”按钮时,我发现您无法使用 Boost。可以使用功能包吗?我认为它有 std::tr1::tuple。

【讨论】:

  • @Éric - std::TR1 似乎不在我们的 SDK 中。澄清一下,我可以使用标准库以及 VS2008 编译器可用的 Windows SDK 和 C++ 语言功能。我相信 boost 会编译并运行。我就是不能使用它(是的,我也讨厌这个限制)。因此,我可能需要实现一些 voodo 的一小部分,即 boost.MPL 来完成这项工作。我只是不知道那是什么。
【解决方案3】:

上一次我需要做这样的事情时,我编写了一个 perl 脚本来为我生成模板。

缺点是您最终需要编译大量代码,但这取决于您的最大值。

【讨论】:

    猜你喜欢
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多