隐藏可变参数模板实现

时间:2017-05-29 19:57:33

标签: c++ c++11 variadic-templates pimpl-idiom

我有一些3rdParty库,其方法如下:

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

它需要一个内部类型的数组(可转换为任何原始的c ++类型)和arg count作为其内部参数。 在我的代码中,我写了一些通用的帮助器来避免手动创建并为每次调用键入convertion:

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 :: Value 使用某种疙瘩成语或代理对象。我知道在c ++中使用虚拟模板方法创建代理或者只是将模板实现移动到.cpp是不可能的,所以我完全坚持这个问题。

将不胜感激任何帮助)

2 个答案:

答案 0 :(得分:2)

不确定。只需写下std::variant<int, double, char, every, other, primitive, type>的等效内容。

现在,您的Invoke会将您的args转换为这些变种的数组(向量,范围等)。

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

然后,内部调用方法使用等效的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");

答案 1 :(得分:1)

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

所以,你可以这样做,但是第三方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)) {}
};