隐藏可变模板实现
Hiding variadic template implementation
我有一些 3rdParty 库,其方法如下:
bool Invoke(const char* method, Value* args, size_t nargs)
它采用其内部类型的数组(可转换为任何原始 C++ 类型)和 arg 计数作为其内部参数。
在我的代码中,我写了一些通用的帮助程序来避免为每次调用手动创建和类型转换:
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 代码并直接连接到我的主项目。
是否可以隐藏此第 3 方库的实现细节和用法? (使用某种 pimple 习语或 3rdParty::Value 的代理对象)。我知道在 C++ 中使用虚拟模板方法创建代理或简单地将模板实现移动到 .cpp 是不可能的,所以我完全被这个问题困住了。
将不胜感激)
如果你想避免暴露 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)) {}
};
当然可以。只需写出 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");
我有一些 3rdParty 库,其方法如下:
bool Invoke(const char* method, Value* args, size_t nargs)
它采用其内部类型的数组(可转换为任何原始 C++ 类型)和 arg 计数作为其内部参数。 在我的代码中,我写了一些通用的帮助程序来避免为每次调用手动创建和类型转换:
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 代码并直接连接到我的主项目。
是否可以隐藏此第 3 方库的实现细节和用法? (使用某种 pimple 习语或 3rdParty::Value 的代理对象)。我知道在 C++ 中使用虚拟模板方法创建代理或简单地将模板实现移动到 .cpp 是不可能的,所以我完全被这个问题困住了。
将不胜感激)
如果你想避免暴露 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)) {}
};
当然可以。只需写出 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");