2017-11-07 7 views
1

私は2つの異なるプロトコルを扱うことができる通信クラスを持っています。プロトコルは、列挙型テンプレート変数によって選択されます。 2つのプロトコルのうち1つは2バイト値のみをサポートし、もう1つは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; 
    } 
}; 

EDIT:1つの答えを正しいものとして選ぶことができます。そして、私はRoyから最も多くを学んだが、できるだけシンプルにするため、MMの答えを選んだ。 (かかわらず、両方のupvoted)

+2

最終的な倍精度浮動小数点型(long)整数を浮動小数点数にダウンキャストするだけの理由はなぜですか?たぶんfloatを受け入れ、必要に応じてクラスを使用するときに正確に何が起きるかを最終的なコンパイラの警告で正確に伝えることが最善の方法でしょう。 –

+0

@MichaelRoyこのライブラリはArduinoユーザーを対象としています。私は彼らがコンパイラの警告(デフォルトでは無効になっている)を見るのは間違いです。しかし、あなたは非常に有効なポイントを持っています!私はプログラムのデザインを調べます。 – BMelis

+1

'((P == P1)?2:4)'おそらく –

答えて

4

これにアプローチする方法はいくつかあります...ここ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. 
{ ... }; 
関連する問題