我正在寻找一种方法来提取类型std::tuple
以定义方法签名。采取以下(人为的)示例:
template <typename RetT, typename... ArgsT>
class A
{
public:
typedef RetT ReturnType;
typedef std::tuple<ArgsT...> ArgTypes;
RetT doSomething(ArgsT... args)
{
// Doesn't make much sense, but it's just an example
return (RetT) printf(args...);
}
};
template <typename Enable, typename RetT, typename... ArgsT>
class AAdapter;
// Simply pass arguments along as-is
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<!std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, ArgsT...> {};
// Add additional first argument if RetT is float
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, const char*, ArgsT...> {};
template <typename RetT, typename... ArgsT>
class B
{
public:
typedef AAdapter<void, RetT, ArgsT...> AAdapter;
// This needs to have the same method signature (return type and argument types) as AAdapter::doSomething()
template <size_t... Index>
typename AAdapter::ReturnType doSomething (
typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args
) {
return a.doSomething(args...);
}
public:
AAdapter a;
};
int main(int argc, char** argv)
{
// I would like to be able to remove the <0,1,2> and <0,1,2,3> below.
B<int, const char*, int, int> b1;
b1.doSomething<0,1,2>("Two values: %d, %d\n", 1, 2);
B<float, const char*, int, int> b2;
b2.doSomething<0,1,2,3>("Three values: %s, %d, %d\n", "a string", 1, 2);
return 0;
}
考虑 AAdapter 更改、添加或删除不透明参数类型的方式。基本上,我想B::doSomething()
简单地重定向到B::AAdapter::doSomething()
,所以我希望这两种方法都具有完全相同的签名。问题是:如何B::AAdapter::doSomething()
从 inside获取参数类型B
?
我B::doSomething()
在上面的代码中的定义是我来得最远的:我正在std::tuple
使用里面的参数类型进行类型定义A
,所以我可以将它们解包回B
. 不幸的是,通过上述方法,我仍然需要Index...
在调用时手动提供模板参数B::doSomething()
。当然,必须有一种方法可以Index...
从元组的大小中自动推断出这些参数。我曾考虑过使用 的方法std::make_integer_sequence
,但这需要我为序列本身定义一个额外的方法参数(并且它不能是具有默认值的最后一个参数,因为在参数包之后不允许使用其他参数)。
有没有什么办法可以做到这一点,不管有没有 std::tuple?需要 C++17 的解决方案会很好。
编辑1:
我现在意识到,我可以通过B
继承而AAdapter
不是将AAdapter
对象作为成员来规避特定应用程序中的问题,但我仍然想知道如何在不必这样做的情况下解决问题。
编辑2:
也许一些关于为什么AAdapter
存在以及我想要实现的附加信息。我正在围绕现有的 C API 实现一种包装类,实际上需要在另一个进程中调用,RPC 样式。因此,如果用户想在远程进程中调用 C 函数,他们将改为在本地调用我的包装类中的相应方法,该方法处理所有 RPC 内容,如类型转换、实际远程调用和其他丑陋的细节。这个包装类B
在我上面的代码中表示为。现在我的包装方法签名通常不会具有与 C 函数完全相同的签名。例如,包装器可能具有而不是C 函数具有std::string_view
的一对。const char*, size_t
由于这里不重要的原因,它还需要有一个输出参数(一个指针),而 C 函数有时有一个返回值。
为了让我不必定义两个单独的方法签名(实际上是三个)并编写代码来转换每个单独的参数,我只将其中一个签名作为模板参数RetT, ArgsT...
传递给B
. 一个签名转换类(AAdapter
在上面的例子中)然后应用规则如何通过添加参数、更改它们的类型等从第一个签名自动生成第二个签名。A
然后将保存这个生成的签名,并B
拥有我提供的签名最初。但是,我想B
提供一个invoke()
带有 签名的方法A
,从而完全隐藏A
用户的整个方法签名。这就是为什么我需要访问模板参数类型A
从内部B
,以及为什么我不能简单地移除中产阶级AAdapter
。