是否可以使用c++20之前的基类类型专门化一个c++模板?

问题描述 投票:0回答:2
template<typename T>
struct is_message : std::false_type {};


template<typename T>
class Publisher {
static_assert(is_message<T>::value, "This message type is not supported");
public:
  void Publish(T& msg) {}
};

之前的代码是来自第三方库的简化草图(ROS2 很简陋,我们不应该更改此代码库), 它使用类型特征

is_message
将消息类型限制为其生成的 IDL C++ 消息,该消息生成代码来专门化先前的类型特征模板,如下所示:

class GeneratedIdlMessageX {
public:
};
template<> struct is_message<GeneratedIdlMessageX> : std::true_type {};

然后用户可以像这样删除正在工作的发布者:

#include <ros2_headers.h>
#include <ros2idl_generated_message.h>

Publisher<GeneratedIdlMessageX> publisher;

现在的问题是我们也想支持 protobuf 消息,但我们无法修改 protobuf IDL 代码生成来添加此专门化。 因此,我们必须在每个 protobuf 消息发布声明上手动添加此专业化,如下所示:

#include <ros2_headers.h>
#inlcude <protobuf_generated_message.h>

template<> struct is_message<ProtoBufMessageY> : std::true_type {};

Publisher<ProtoBufMessageY> publisher;

怎样才能省掉这个烦人的工作呢?

一个直接的想法是引入一个预定义的通用标头,它将所有 protobuf 生成的消息专门用于发布者用户。 考虑到所有protobuf生成的c++消息都是从

MessageLite
继承的,我尝试直接专门化基类,显然它不起作用:

// of course this won't work
template <> struct is_message<protobuf::MessageLite> : std::true_type{};

现在我因为无法修改第三方(ros2)和 protobuf IDL 生成代码而陷入困境,感谢任何帮助。

注意:编译和运行环境限制c++标准低于c++17(c++20概念不可用)。

c++ templates c++17 protocol-buffers ros2
2个回答
2
投票

C++20 概念的救援:

template <std::derived_from<protobuf::MessageLite> T>
struct is_message<T> : std::true_type {};

在 C++17 及更早版本中,这只能通过主模板的配合来实现,主模板必须包含虚拟模板参数:

template <typename T, typename = void>
struct is_message : std::false_type {};

template <typename T>
struct is_message<T, std::enable_if_t<std::is_base_of_v<protobuf::MessageLite, T>>>
    : std::true_type {};

如果库缺少此模板参数,最好将其报告为错误。


0
投票

在 C++20 中,您可以使用其他一些解决方案。不幸的是,你的双手被 C++17 束缚了。

一个非常肮脏的解决方案是这样的:

template <typename T>
struct Message;

template<typename T>
struct is_message<Message<T>> : std::true_type {};

template <typename T>
class Publisher<Message<T>> : public Publisher<T> {
public:
    using Publisher<T>::Publisher; // inherit all constructors
};

您为

is_message
Publisher
定义部分特化,以便
is_message
检查成功。 然后可以像这样使用:

Publisher<Message<ProtoBufMessageX>> publisher;

此解决方案显然有一个缺点,即您需要对所有协议缓冲区消息使用

Message
包装器。 为了隐藏这个丑陋之处,您可以添加一个方便的别名:

// If upgraded to C++20, this could eventually be turned into just
//   = Publisher<T>
template <typename T>
using PublisherType = Publisher<Message<ProtoBufMessageX>>;

PublisherType<ProtoBufMessageX> publisher;

另一个选项是分别为每条消息定义显式专业化,就像您建议的那样:

template<> struct is_message<ProtoBufMessageX> : std::true_type {};
template<> struct is_message<ProtoBufMessageY> : std::true_type {};
template<> struct is_message<ProtoBufMessageZ> : std::true_type {};
// ...
© www.soinside.com 2019 - 2024. All rights reserved.