2017-05-29 77 views
3

我有一些的3rdParty库这样的方法:隐藏可变参数模板实施

bool Invoke(const char* method, Value* args, size_t nargs) 

它需要其内类型的数组(可转换至任何原语C++类型)和Arg计数作为其内PARAMS。 在我的代码,我写了一些通用的辅助,以避免每个调用手动创建和类型的皈依:

template<class ... Args> 
bool WrappedValue::Invoke(const char* method, Args&& ... args) 
{ 
    3rdParty::Value values[] = 
    { 
     3rdParty::Value(std::forward<Args>(args))... 
    } 

    return m_value.Invoke(method, values, sizeof ... (Args)); 
} 

它工作得很好,但现在我要在我的头文件中定义的3rdParty代码和lib直接连接到我的主要项目。

是否可以隐藏该第三方库的实现细节和用法? (使用某种疙瘩成语或代理对象3rdParty ::价值)。我知道无法在C++中使用虚拟模板方法来创建代理或仅将模板实现移动到.cpp,因此我完全停留在这个问题上。

将是任何帮助感激)

回答

2

当然。简单的写下相当于std::variant<int, double, char, every, other, primitive, type>

现在您的Invoke将您的args转换为这些变体的数组(矢量,跨度,任何)。

然后你将这个变量数组传递给你的内部调用方法。

该内部调用方法然后使用等效的std::visit从每个变体中生成3rdParty::Value

Boost提供了一个可能工作的boost::variant

你也可以手动滚动。通过狭隘地指出你的问题,你会得到比std::variant更简单的东西。但是,这不仅仅是一点工作。


另一种方法是这样的

template<class T> struct tag_t {constexpr tag_t(){}; using type=T;}; 
template<class T> constexpr tag_t<T> tag{}; 

template<class T, class F, class ... Args> 
bool WrappedValue::Invoke(tag_t<T>, F&& f, const char* method, Args&& ... args) 
{ 
    T values[] = { 
    T(std::forward<Args>(args))... 
    }; 

    return std::forward<F>(f)(method, values, sizeof...(Args)); 
} 

这是简单的。在这里,你会写:

bool r = Invoke(tag<3rdParty::Value>, [&](const char* method, 3rdParty::Value* values, std::size_t count) { 
    m_value.Invoke(method, values, count); 
}, 3.14, 42, "hello world"); 
+0

你的第二个方法公开的3rdParty API,即'的3rdParty :: Value'在实际'的invoke()'调用,其中OP希望避免。 – Walter

+0

@Walter第一种方法隐藏调用者的第三方API,第二种方法本身。我不确定OP是想要的;这是“主要项目”。所以我包含了两个选项。 – Yakk

1

如果你想避免暴露的3rdParty API,你需要一些非模板的方法来传递数据。这不可避免地需要一些类型擦除机制(如std::any),而这些机制将暴露在您的API中。

所以,你能做到这一点,但随后的3rdParty Value已经是一个类型擦除方法,这只会从一种类型擦除的数据传递到下一个,产生额外的开销。无论这个价格是否值得付费,只有您可以决定。

我不知何故忽略了你的评论,即参数都是原始的。在这种情况下,类型擦除要简单得多,可以通过标记+联合来完成,如

struct erasure_of_primitive 
{ 
    enum { is_void=0, is_str=1, is_int=2, is_flt=3, is_ptr=4 } 
    int type = is_void; 
    union { 
    const char*s; // pointer to external C-string 
    int64_t i;  // any integer 
    double d;  // any floating point number 
    void*p;  // any pointer 
    } u; 

    erasure_of_primitive() = default; 
    erasure_of_primitive(erasure_of_primitive&const) = default; 
    erasure_of_primitive&operator=(erasure_of_primitive&const) = default; 

    erasure_of_primitive(const char*str) 
    : type(is_str), u.s(str) {} 

    template<typename T> 
    erasure_of_primitive(T x, enable_if_t<is_integer<T>::value>* =0) 
    : type(is_int), u.i(x) {} 

    template<typename T> 
    erasure_of_primitive(T x, enable_if_t<is_floating_point<T>::value>* =0) 
    : type(is_flt), u.d(x) {} 

    template<typename T> 
    erasure_of_primitive(T*x) 
    : type(is_ptr), u.p(static_cast<void*>(x)) {} 
};