0

我正在试验并尝试制作一个基于模板策略的元库。示例案例是为设备驱动程序聚合 2 个类。类实现device_logicand connection_logic,它们不需要依赖于彼此的类型:

  • 设备逻辑仅取决于通信协议(消息)。
  • connection_logic 只是字节数组的来源,可能使用不同类型的连接:SerialPort、tcp、udp、自定义 PCI Express 设备等。

目标不是在它们上强制使用任何接口或类型。它们必须完全依赖于 API 规范,并且只提供必要的特征。

STL 方法是在标头中定义特征,然后在类中使用它们。所以特征标签必须定义在模板库的头文件中。

// device_traits.h

namespace traits
{

   // tags to be defined as io_type
   struct writeable;
   struct readable;
   struct wretableReadable;


   template <typename T>
   constexpr bool is_writeable()
   {
       return std::is_same_v<writeable, typename T::io_type>() ||
              std::is_same_v<wretableReadable, typename T::io_type>();
   }

   // functions for readable and readableWriteable
      
}

template <typename ConnectionLogic,
          typename DeviceLogic>
class aggregate_device
{

static_assert(!traits::readable<DeviceLogic>() ||
              (traits::readable<DeviceLogic>() &&
               traits::readable<ConnectionLogic>()),
               "Device logic is readable so must be ConnectionLogic");

static_assert(!traits::writeable<DeviceLogic>() ||
              (traits::writeable<DeviceLogic>() &&
               traits::writeable<ConnectionLogic>()),
               "Device logic is writeable so must be ConnectionLogic");

};

在这种情况下aggregate_device,聚合连接和设备逻辑。如果设备逻辑可读,则连接逻辑必须提供输入。如果设备逻辑是可写的,则连接必须提供输出。

// device_logic.h
#include <device_traits>

class device_logic
{
public:
   using io_type = traits::readableWriteable;
   // ... methdos, etc
};

此版本有效,但引入了对模板库的依赖。引入依赖项(甚至是仅包含头文件的库)对开发人员来说并不方便,而且通常对库也不利。有人可能想device_logic在另一个模块或项目中使用类,但不想拉取它所依赖的模板库。

消除依赖关系的另一个解决方案不是强制类提供者将io_type标签注入他的类,而是自己定义它们。

// device_traits.h

namespace traits
{

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

   // here we just check if a typename has a type writeable
   template<typename T>
   struct is_writeable<T, std::void_t<typename T::writeable>> : std::true_type{};

   // functions for readable and readableWriteable
      
   // aggregator class
}

// device_logic.h
// don't include nothing


class device_logic
{
   public:

   // define a type 
   struct writeable;
};


/////
#include <device_traits>

static_assert(traits::is_writeable<device_logic>(), "");

现在我使用第二种方法并且它有效。问题是:

  • 这是一种合法的方法吗?
  • 对于类提供者来说,这不会令人困惑吗?
  • 会(在多大程度上)更难维护?
  • 编译性能可能有什么不同?
4

1 回答 1

2

这是一种合法的方法吗?
对于类提供者来说,这不会令人困惑吗?

标准使用不同的方法:

  • 存在类型,例如应该具有“类型”的透明比较器is_transparent(如using is_transparent = void;

  • 特定标签为iterator_tags

  • 甚至只是鸭子打字(不检查模板)

  • 或 SFINAE 关于方法/属性的存在。

这些类型可能是:

  • 课堂内(至于is_transparent
  • 或作为外部特征提供,例如std::iterator_traits(它甚至允许在可能的情况下从类中提取内部 typedef)。

int请注意,只有外部特征可能以非侵入方式支持内置类型(指针、 ...)或外部类型(第三个库或您的特征的标准库)。

会(在多大程度上)更难维护?

之间存在权衡

  • “物理”依赖关系,其中的东西,因此,更链接,并且可能更简单地保持同步,但创建一个依赖关系。

  • 没有“物理”依赖,因此可能更难保持同步。

编译性能可能有什么不同?

与往常一样,您必须进行测量。

例如build-bench.com

要一起使用,您似乎必须包含类似的代码,但不需要以相同的顺序,所以我敢打赌类似的性能。

当独立使用时,你应该避免一个额外的#include(所以取决于它的大小/数量#include,如果使用 pch,......)......

于 2021-02-22T10:37:04.347 回答