Zero-dependency 特征定义

Zero-dependency traits definition

我正在试验并试图制作一个模板 policy-based 元库。示例案例是为设备 driver 聚合 2 classes。 classes 实现了 device_logicconnection_logic,它们不需要依赖彼此的类型:

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

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

// 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
};

此版本有效,但引入了对模板库的依赖。引入依赖项(即使是 header-only 库) 对开发人员来说并不方便,而且通常对库也不利。有人可能想在另一个模块或项目中使用 device_logic class,但不想拉取它依赖的模板库。

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

// 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>(), "");

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

Is it a legit approach?
Wouldn't it be confusing for a class provider?

标准使用不同的方法:

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

  • 特定标签为 iterator_tags

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

  • 或 SFINAE 存在 method/properties。

这些类型可能是:

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

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

Will it be (at what extent) harder to maintain?

之间存在取舍
  • “物理”依赖性,东西之间的联系更多,保持同步可能更简单,但会产生依赖性。

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

What may be the differences in performance for compiling?

一如既往,你必须测量。

例如 build-bench.com.

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

单独使用时,应避免多出一个#include(所以要看#include的size/number,如果使用pch,...)...