2012-10-07 53 views
1

我想弄清楚如何創建一個C++ 11模板函數,它可以在兩個約定之間轉換函數調用:第一個使用Variant(注意:變體是一個多態類型,它是子類IntVariable,DoubleVariant等的基礎),第二個是C函數調用。C++ 11 variadic模板函數調用轉發

我們知道編譯時的每一條信息:參數count是參數個數,參數/返回類型取決於'cfunc'變量類型。

// We will assume that the two following functions are defined with their correct 
// specializations. 

template < typename T > 
Variant * convertToVariant(T t); 

template < typename T > 
T convertFromVariant(Variant * variant); 

// The following function is incomplete, the question is how to convert the 
// variant parameters into a C function call ? 

template < typename Return, typename... Arguments > 
Variant * wrapCFunction< Return cfunc(Args...) >(int argc, Variant ** argv) { 
    // Here comes the magic call of cfunc, something like : 
    if (argc != mpl::count<Args...>::value) 
     throw std::runtime_error("bad argument count"); 
    return cfunc(convertFromVariant<Args...>(argv[ X ])...); 
} 

// Example use case : 

int foo(int a, int b); 

int main(void) { 
    int argc = 2; 
    Variant * argv[2] = { new IntVariant(5), new IntVariant(6) }; 

    Variant * res = wrapCFunction<foo>(argc, argv); 
    IntVariant * intRes = dynamic_cast<IntVariant>(res); 

    return intRes ? intRes->value : -1; 
} 
+0

出於好奇,爲什麼地球上會需要運行時變體而不是編譯時間, [Boost.Variant](http://www.boost.org/libs/variant/)? – ildjarn

+0

@ildjarn我正在使用運行時變體來保存動態語言的運行時庫中的值(如Zend引擎中的ZValues)。 –

+0

最佳答案我能想象,只是想我會問。 : - ] – ildjarn

回答

5

使用indices trick,這是相當容易:

template<unsigned...> struct indices{}; 

template<unsigned N, unsigned... Is> 
struct indices_gen : indices_gen<N-1, N-1, Is...>{}; 

template<unsigned... Is> 
struct indices_gen<0, Is...> : indices<Is...>{}; 

// assuming the parameters were actually like this 
template<typename Return, typename... Args, unsigned... Is> 
Variant* wrapCFunction(Return (*cfunc)(Args...), int argc, Variant** argv, indices<Is...>) { 
    return cfunc(convertFromVariant<Args>(argv[Is])...); 
} 

template<typename Return, typename... Args> 
Variant* wrapCFunction(Return (*cfunc)(Args...), int argc, Variant** argv) { 
    if (argc != sizeof...(Args)) 
     throw std::runtime_error("bad argument count"); 
    return wrapCFunction(cfunc, argc, argv, indices_gen<sizeof...(Args)>()); 
} 

注意,在代碼中的一些變化。首先,sizeof...(Args)會生成包中的參數個數。其次,我將該函數的簽名固定爲通過cfunc作爲實際參數。

+1

哇,謝謝,不知道這個伎倆。我真的很喜歡C++ 11 ...但是,C++元編程仍然需要這麼多的黑魔法很有趣:) –

+1

@Nison:我不會把它稱爲「黑魔法」,因爲訣竅本身相當簡單瞭解。你基本上生成一個可變參數包索引。這一代是通過遞增遞減較小的索引來遞歸完成的,直到達到「0」。 – Xeo

2
class Variant; 
template < typename T > Variant * convertToVariant(T t); 
template < typename T > T convertFromVariant(Variant * variant); 

template <typename Return, typename... Arguments> 
struct WrapCFunctionImpl { 
    template<int argsToAdd, int... argIndexes> 
    struct Impl { 
    typedef typename Impl<argsToAdd - 1, argIndexes..., sizeof...(argIndexes)>::Type Type; 
    }; 
}; 
template <typename Return, typename... Arguments> 
template <int... argIndexes> 
struct WrapCFunctionImpl<Return, Arguments...>::Impl<0, argIndexes...> { 
    typedef Impl Type; 
    static Variant* run(Return cfunc(Arguments...), Variant ** argv) { 
    return convertToVariant(cfunc(convertFromVariant<Arguments>(argv[argIndexes])...)); 
    } 
}; 

template < typename Return, typename... Arguments > 
Variant * wrapCFunction(Return cfunc(Arguments...), Variant ** argv) { 
    return WrapCFunctionImpl<Return,Arguments...>::template Impl<sizeof...(Arguments)>::Type::run(cfunc, argv); 
} 

int foo(int, int); 
Variant *f(Variant** x) { 
    return wrapCFunction(foo, x); 
} 

這裏的大多數困難的是生成索引到數組遞歸。可能有更簡單的方法來做到這一點,但這是有效的。

+0

你的代碼比Xeo更復雜一些,但是謝謝! :) –