【发布时间】:2020-08-12 00:54:27
【问题描述】:
我正在寻找一种方法来提取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 内部获取B::AAdapter::doSomething() 的参数类型?
我在上面的代码中对B::doSomething() 的定义是我所了解的最远的:我正在使用A 中的参数类型定义std::tuple,因此我可以将它们解压缩回参数包B。不幸的是,通过上述方法,我仍然需要在调用B::doSomething() 时手动提供Index... 模板参数。肯定有办法让这些Index... 参数自动从元组的大小中推导出来。我已经考虑过使用std::make_integer_sequence 的方法,但这需要我为序列本身定义一个额外的方法参数(并且它不能是具有默认值的最后一个参数,因为在参数包之后不允许使用其他参数) .
有没有什么方法可以做到这一点,不管有没有 std::tuple?需要 C++17 的解决方案就可以了。
编辑 1:
我现在意识到,我可以通过从 AAdapter 获得 B inherit 而不是将 AAdapter 对象作为成员来规避我的特定应用程序中的问题,但我仍然会想知道如何解决问题而不必这样做。
编辑 2:
也许有一些关于为什么AAdapter 存在以及我想要实现什么的额外信息。我正在围绕现有的 C API 实现一种包装类,实际上需要在另一个进程中调用,RPC 样式。因此,如果用户想在远程进程中调用 C 函数,他们将改为在本地调用我的包装类中的相应方法,该方法处理所有 RPC 内容,如类型转换、实际远程调用和其他丑陋的细节。这个包装类在我上面的代码中由B 表示。现在我的包装方法签名通常不会具有与 C 函数完全相同的签名。例如,包装器可能具有std::string_view,而不是C 函数具有的一对const char*, size_t。由于这里不重要的原因,它还需要有一个输出参数(一个指针),而不是 C 函数有一个返回值。
为了让我不必定义两个单独的方法签名(实际上是三个)并编写代码来转换每个单独的参数,我只将其中一个签名作为模板参数 RetT, ArgsT... 传递给B。签名转换类(在上面的示例中为AAdapter)然后应用规则,如何通过添加参数、更改它们的类型等从第一个签名自动生成第二个签名。A 然后将保存这个生成的签名,并且B 将拥有我最初提供的那个。但是,我希望B 提供带有A 签名的invoke() 方法,从而将A 和整个方法签名完全隐藏在用户面前。这就是为什么我需要从B 中访问A 的模板参数类型,以及为什么我不能简单地删除中间类AAdapter。
【问题讨论】:
-
您可以使用
std::tuple_element_t<index, tuple_type>获取类tuple_type 中的索引类型。 -
@Anonymous1847 这就是我在示例中尝试执行的操作,但我需要访问所有元素,而不仅仅是具有特定索引的元素,并在调用者无需指定的情况下生成此索引列表手动操作基本上就是我要问的问题。
标签: c++ c++17 template-meta-programming stdtuple parameter-pack