提问人:NetoBF 提问时间:11/10/2023 更新时间:11/10/2023 访问量:42
创建一个pod_tuple类,它是标准布局,可以通过一些类型修改轻松复制
creating an pod_tuple-class which is standard layout and trivially copyable with some type-modifications
问:
我正在尝试编写一个易于复制和标准布局的 pod_tuple 类(当然,前提是这也适用于类型本身)。它应该感觉与 std::tuple 相同,但只需要创建并具有类似于使用 std::get 的索引序列包扩展。 这是必需的,因为我们有一个架构,用户可以使用参数包调用被调用者,但他不需要知道它实际调用了什么。我们有一个多核架构(每个内核具有相同的 Arm 架构和编译器),并提供 IPC。这意味着每个内核上都有一个单独运行的编译实例。
在一些文章和 ChatGPT 的帮助下,我设法编写了一个可以用作pod_tuple的类。
有几点需要澄清,我将从我得到的草稿开始:
1.递归没有正确结束
#include <cstring>
#include <type_traits>
#include <functional>
#include <cstdio>
template<typename... Ts>
struct pod_tuple;
template<>
struct pod_tuple<> {
// empty tuple
};
template<typename T, typename... Ts>
struct pod_tuple<T, Ts...> : pod_tuple<Ts...> {
// tuple with at least one element
typedef typename std::remove_reference_t <T> TNoRef;
TNoRef value;
// static_assert(std::is_trivially_copyable<TNoRef>::value, "pack types must be trivially copyable");
// static_assert(std::is_standard_layout<TNoRef>::value, "pack types must be standard layout");
// default constructor
pod_tuple() = default;
// constructor from values
pod_tuple(T t, Ts... ts) : pod_tuple<Ts...>(ts...), value(t) {}
// copy constructor
pod_tuple(const pod_tuple& other) = default;
// copy assignment operator
pod_tuple& operator=(const pod_tuple& other) = default;
// destructor
~pod_tuple() = default;
};
//// helper function to get the element of a tuple by index
template<std::size_t I, typename T>
struct tuple_element;
template<typename T, typename... Ts>
struct tuple_element<0, pod_tuple<T, Ts...>> {
using type = T;
};
template<std::size_t I, typename T, typename... Ts>
struct tuple_element<I, pod_tuple<T, Ts...>> {
using type = typename tuple_element<I-1, pod_tuple<Ts...>>::type;
};
template<std::size_t I, typename T>
using tuple_element_t = typename tuple_element<I, T>::type;
template<std::size_t I, typename T, typename... Ts>
tuple_element_t<I, pod_tuple<T, Ts...>>& get(pod_tuple<T, Ts...>& t) {
return get<I-1>(static_cast<pod_tuple<Ts...>&>(t));
}
template<typename T, typename... Ts>
T& get(pod_tuple<T, Ts...>& t) {
return t.value;
}
使用它:
template<typename... TParams>
void call(TParams... args)
{
printf("Iamcalled!");
}
template <std::size_t... Is, typename... TParams>
void callWithTuple(pod_tuple<TParams...>& tuple, std::index_sequence<Is...> /* dummy */)
{
call(get<Is>(tuple)...);
}
int main()
{
pod_tuple<int, char> myTuple(3,'s');
callWithTuple(myTuple, std::index_sequence_for<int, char>());
}
我得到的错误是基于到达的位置,而我没有到达递归的终点。相反,当遵循 get-recursion 时,它会从 0 中减去 1:I-1
I
0
tuple_element<0, pod_tuple<T, Ts...>>
<source>: In instantiation of 'struct tuple_element<18446744073709551615, pod_tuple<char> >':
<source>:54:7: required by substitution of 'template<long unsigned int I, class T> using tuple_element_t = typename tuple_element::type [with long unsigned int I = 18446744073709551615; T = pod_tuple<char>]'
<source>:57:42: required by substitution of 'template<long unsigned int I, class T, class ... Ts> tuple_element_t<I, pod_tuple<T, Ts ...> >& get(pod_tuple<T, Ts ...>&) [with long unsigned int I = 18446744073709551615; T = char; Ts = {}]'
<source>:58:20: required from 'tuple_element_t<I, pod_tuple<T, Ts ...> >& get(pod_tuple<T, Ts ...>&) [with long unsigned int I = 0; T = int; Ts = {char}; tuple_element_t<I, pod_tuple<T, Ts ...> > = int]'
<source>:75:17: required from 'void callWithTuple(pod_tuple<Ts ...>&, std::index_sequence<Is ...>) [with long unsigned int ...Is = {0, 1}; TParams = {int, char}; std::index_sequence<Is ...> = std::integer_sequence<long unsigned int, 0, 1>]'
<source>:82:18: required from here
<source>:50:11: error: invalid use of incomplete type 'struct tuple_element<18446744073709551614, pod_tuple<> >'
50 | using type = typename tuple_element<I-1, pod_tuple<Ts...>>::type;
示例如下:https://godbolt.org/z/PGxMh9bbe
我对模板元编程没有那么深入,但我不明白为什么没有到达递归的终点。
2. 删除和重新添加引用
我进一步的想法是,一旦递归起作用,我想自动删除,然后再次添加引用。
在示例代码 1.您可以找到:pod_tuple
typedef typename std::remove_reference_t <T> TNoRef;
TNoRef value;
我在那里添加了。到目前为止一切顺利,这删除了引用。记住我们的用例(将 via IPC 转移到另一个内核),我们知道其他内核具有完全相同的匹配模板声明,这确保了参数包类型的定义与发送内核完全相同(两个内核的编译器和代码库相同)。因此,接收核心代码知道是否有引用。
如果我现在想使用(在接收核心上)调用使用引用类型的,我想这不会那样工作吗?我想我需要另外定义一个类似函数的函数,该函数首先删除引用,然后在接收核心上添加引用?还是带有参考的类型定义就足够了?我认为不会,因为将直接通过它从模板参数包中知道的类型插入序列元素,而不是从已删除引用的pod_tuple中插入序列元素。那么我怎样才能再次添加它们呢?pod_tuple
pod_tuple
callWithTuple
pod_tuple
make_tuple
make_tuple
callWithTuple
3. 自动调用pod_tuple传递类型的函数
我们还使用 etl(嵌入式模板库)。
它们提供了一个字符串类 (https://www.etlcpp.com/string.html),供我们使用。这个字符串类可以通过 memcpy 复制(但失败了!但之后你需要调用一个函数。由于我想向用户隐藏实现,因此不会通知他调用是来自自己的内核还是来自另一个内核。所以他不会知道他是否必须打电话。这也适用于其他一些 etl 容器。
我看到了这个帖子:如何检查模板化类是否具有成员函数?并提出了对 C++14 的修改(链接中的第二个答案):std::is_trivially_copyable
repair()
repair()
namespace detail{
template<class>
struct sfinae_true : std::true_type{};
template<class T>
static auto test_repair(int)
-> sfinae_true<decltype(std::declval<T>().repair())>;
template<class>
static auto test_repair(long) -> std::false_type;
} // detail::
template<class T>
struct has_repair : decltype(detail::test_repair<T>(0)){};
不幸的是,由于之前的编译错误,我没有进一步测试它。但我的想法是像索引序列一样使用它,同时在接收时遍历元组中的所有类型(甚至可能在 -function 中),并在匹配的情况下调用函数(like )。
但这听起来有点容易。是否可以在编译时实现这样的目标?callWithTuple
repair()
T::repair()
std::enable_if
提前致谢。
答: 暂无答案
评论