【问题标题】:Change behavior based on template enum parameter根据模板枚举参数更改行为
【发布时间】:2017-11-07 21:17:31
【问题描述】:

我有一个可以处理 2 种不同协议的通信类。协议由枚举模板变量选择。 现在 2 个协议中的 1 个仅支持 2 字节值,而另一个支持 4 字节值。 通信是通过模板成员函数完成的。如何更改 static_assert 使其占用 2 或 4 个字节,具体取决于类专业化(= 选择的协议)?

#include <iostream>
#include <math.h>

enum Protocol { P1, P2 };


template <Protocol P>
class Communicator {
  public:
    template <typename T>
    void communicate(T arg) {
      static_assert(sizeof(arg) <= sizeof(float), "argument size must be <= 4 bytes");
      float value = (float)arg;
      uint8_t length = sizeof(arg);  //length in bytes
      _communicate(value, length);
    }

    void _communicate(float f, uint8_t length) {
      std::cout << f;
    }
};

编辑:我只能选择 1 个正确答案。尽管我从 Roy 那里学到了最多的东西,但我还是选择了 MM 的答案,因为它使事情尽可能简单。 (虽然都赞成)

【问题讨论】:

  • 如果只是将最终的双精度或(长)长整数向下转换为浮点数,为什么还要使用模板?也许最好只接受一个浮点数并让最终的编译器警告告诉调用者在使用该类时他的精度会发生什么,如果需要的话。
  • @MichaelRoy 该库适用于 Arduino 用户。我怀疑他们会查看编译器警告(默认情况下禁用)。但是你有一个非常有效的观点!我会研究程序的设计。
  • ((P == P1) ? 2 : 4) 可能也可以
  • @M.M 真的就这么简单吗? (因为你说“可能”)
  • MM有一个非常有效的观点。

标签: c++ templates enums template-specialization


【解决方案1】:

有几种方法可以解决这个问题...这里是一种:

template<Protocol P>
size_t value_size();

template<> size_t value_size<P1>() { return 2; }
template<> size_t value_size<P2>() { return 4; }

// ... inside your other function
static_assert(sizeof(arg) <= value_size<P>(), 

【讨论】:

    【解决方案2】:

    这是一种不同的方法

    #include <iostream>
    #include <math.h>
    #include <cstdint>
    
    // enum Protocol { P1, P2 };  // let's use types with traits instead.
    
    struct P1
    {
        constexpr static const int protocol_id = 1; 
                               //^^ or maybe use an enum 
                               // type, this may need refactoring
                               // to fit your code and style.
        using data_type = uint16_t;  //< or whatever your 2-byte datatype is.
    
        // can add more data traits and even static member functions here
    
        // you could also add protocol specific non-static data if you add a 
        // P1 data member to your Communicator class.
    
        // A struct with traits is a very good entry point for many compile-time
        // polymorphism techniques.
    };
    
    struct P2
    {
        constexpr static const int protocol_id = 2;
        using data_type = uint32_t;  //< or whatever your 4-byte datatype is.
    };
    
    
    template <typename _Proto>
    class Communicator {
      public:
          using data_type = typename _Proto::data_type;
          constexpr static const int proto_id = typename _Proto::protocol_id;
    
      public:
        void communicate(data_type arg) // we can use the trait from either P1 or P2
       {
          float value = (float)arg;
          uint8_t length = sizeof(data_type);  //length in bytes
          _communicate(value, length);
        }
    
        void _communicate(float f, uint8_t length) 
        {
          std::cout << f;
        }
    };
    

    这是转换枚举的代码(如果这是您已经拥有的类。

    enum protocol_t { p1, p2 };
    
    template <protocol_t _p> struct Protocol {};
    
    // simply derive the template specialization from the desired type
    template <> struct Protocol<p1> : P1 {};
    // simply derive the template specialization from the desired type
    template <> struct Protocol<p2> : P2 {};
    

    您也可以从 P1、P2 派生来帮助组织代码。

    struct P1
    {
       // ... + specialized functions:
       void _communicate(value_type x) { ... }  // that you'll call from Communicator
       void _communicate(const value_type* begin, const value_type* end) { ... }
    };
    
    struct P2 { /* same thing */ };
    
    template <typename _Proto>
    class Communicator : _Proto // < can control visibility here.
    { ... };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-25
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      相关资源
      最近更新 更多