2017-02-22 16 views
1

私はCライブラリによって定義された型で動作するいくつかの関数を持っています。繰り返しテンプレートの特殊化を避けるにはどうすればよいですか?

タイプは、float_2、float_3、float_4、int32_2、int32_3、int32_4です。

これらのタイプには共通のものがあり、sizeという名前のフィールドがあります。 float_2の場合、size = 2、float_3、size = 3など

現在、私はすべての単一タイプに対してテンプレートを特殊化しています。

static void add_number(rapidjson::Value &target, const char* name, float_2 src, Document::AllocatorType& alloc) 
{ 
    std::ostringstream ss; 
    ss << src.x << " " << src.y; 
    std::string s(ss.str()); 
    target.AddMember(StringRef(name), s, alloc); 
} 

static void add_number(rapidjson::Value &target, const char* name, float_3 src, Document::AllocatorType& alloc) 
{ 
    std::ostringstream ss; 
    ss << src.x << " " << src.y << " " << src.z; 
    std::string s(ss.str()); 
    target.AddMember(StringRef(name), s, alloc); 
} 

必ず次の操作を行うことができるようにいいだろう:

template <typename T> 
static void add_number(rapidjson::Value &target, const char* name, T src, Document::AllocatorType& alloc) 
{ 
    std::ostringstream ss; 
    switch(src.size){ 
     case 2: ss << src.x << " " << src.y; 
     case 3: ss << src.x << " " << src.y << " " << src.z; 
     case 4: ss << src.x << " " << src.y << " " << src.z << " " << src.w; 
    } 
    std::string s(ss.str()); 
    target.AddMember(StringRef(name), s, alloc); 
} 

しかし、これらのフィールドが定義されたすべてのタイプのために存在していないので、それは動作しません。あらゆる単一のケースを専門にするのを避けることを可能にするC++の魔法のテンプレート構文がありますか?

+1

したがって、各 'float_'は単一を持っていますパラメータは同じ( 'size')ですが、' x'、 'y'、' z'と異なりますか? – Tas

+0

ここでは、専門化を避けることはできません。何か、どこかに特化しなければならない。特殊化をヘルパークラスに委託し、addMember()呼び出しを行うためのadd_number()を残して、ヘルパークラスを 'src'を' std :: string'に変換することもできますが、それはそれです。 C++コンパイラは、コア言語の一部として人工知能を持っていません。そのような人は、一度に1つのステップを実行する必要があるすべてのことを行うためのコードを書く必要があります。それは、特殊なクラスの形式などです。 –

+0

修正。サイズフィールドは、これらのすべてのタイプに共通です。ただし、Tがfloatの場合は例外です。 – Matt

答えて

2

これは、単独印刷、サイズ、またはその他の特殊化の要件とは関係ありません。しかし、私は、提示されたケースの特殊化の最小量は、異なるケースについてストリーミングオペレータに過負荷をかけることであると私は考える。それは次のようになります。

std::ostream& operator<<(std::ostream &stream, const float_2 &vec) 
{ 
    return stream << vec.x << ", " << vec.y; 
} 

std::ostream& operator<<(std::ostream &stream, const float_3 &vec) 
{ 
    return stream << vec.x << ", " << vec.y << ", " << vec.z; 
} 

std::ostream& operator<<(std::ostream &stream, const float_4 &vec) 
{ 
    return stream << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w; 
} 

template < typename T > 
static void add_number(rapidjson::Value &target, const char* name, const T &src, Document::AllocatorType& alloc) 
{ 
    std::ostringstream ss; 
    ss << src; 
    std::string s(ss.str()); 
    target.AddMember(StringRef(name), s, alloc); 
} 
+0

シンプル!ありがとうございました。それは入力の束を節約しています。 – Matt

1

あなたは確かに「テンプレートの魔法」を使用し、過負荷(セット)(から関数テンプレートを削除)することができます。しかし、10種類以上の異なるタイプがない場合、私はむしろoperator<<過負荷に行くだろう。

namespace detail 
{ 

    template <class T, class = int> struct has_z : std::false_type { }; 
    template <class T> struct has_z <T, decltype((void)T::z, 0)> : std::true_type { }; 

    template <typename T> 
    static auto foo_impl(T src) 
     -> std::enable_if_t<!has_z<T>::value> 
    { 
     std::cout << "1: " << src.x << " " << src.y << "\n"; 
    } 

    template <typename T> 
    static auto foo_impl(T src) 
     -> std::enable_if_t<has_z<T>::value> 
    { 
     std::cout << "2: " << src.x << " " << src.y << " " << src.z << "\n"; 
    } 

} 

template<class T> 
void foo(T src) 
{ 
    detail::foo_impl(src); 
} 

今、あなたは、xを持っているすべてのタイプにfooを使用yし、必要に応じてzメンバーことができます。

struct A { double x{ 1 }, y{ 2 }; }; 
struct B { double x{ 3 }, y{ 4 }, z{ 5 }; }; 

int main() { 
    A a; 
    B b; 
    foo(a); 
    foo(b); 
    return 0; 
} 

プリント

1 2 
3 4 5 
+0

ありがとう!私は最後にストリームオーバーロードバージョンに行ってきました。私はそれが少し短いと思う。 – Matt

関連する問題