2016-09-09 7 views
1

#ifdef FEATURE_A,#ifdef _MSC_VERのようなプリプロセッサブロックが詰まっています。プリプロセッサの#ifdefブロックをテンプレートの実装に置き換えてください。

いくつかのプリプロセッサブロックをテンプレート実装で置き換えるために、コードの一部をリファクタリングしたいと思います。

EDIT: タスクは混乱を取り除くために、すべてのプリプロセッサブロックを削除し、そのうちのいくつかではありません。

私はので、ここで現実の世界(ない私のコード)からの一つだ、foobarの例で穴にあなたをしたくない:

template <typename T> 
std::string demangle() 
{ 
#ifdef __GNUC__ 
    size_t sz; 
    int status; 
    char* ptr = abi::__cxa_demangle(typeid(T).name(), 0, &sz, &status); 
    std::string name(ptr ? ptr : "", ptr ? strlen(ptr) : 0); 
    if(ptr){ free(ptr); } 
    std::string::size_type pos = name.rfind("::"); 
    if(pos != std::string::npos) 
    { 
    name = name.substr(pos + 2); 
    } 
#elif _MSC_VER 
    std::string name(typeid(T).name()); 
    static const std::string struct_prefix("struct "); 
    static const std::string class_prefix("class "); 
    static const std::string ptr_postfix(" *"); 

    std::string::size_type 
    at = name.find(struct_prefix); 
    if(at != std::string::npos) { name.erase(at, struct_prefix.size()); } 
    at = name.find(class_prefix); 
    if(at != std::string::npos) { name.erase(at, class_prefix.size()); } 
    at = name.find(ptr_postfix); 
    if(at != std::string::npos) { name.erase(at, ptr_postfix.size()); } 
#else 
    std::string name(typeid(T).name()); 
#endif 
    return name; 
} 

質問1:equvalentテンプレートの実装にこれを変換する方法を?

質問2:なぜ努力する価値があるのですか、それともなぜですか?

+0

とにかくこれらのプリプロセッサ定義が必要です。たぶんあなたは3つの関数 'std :: string demangle()'を持っているかもしれません。 –

+0

これはとにかく必要ですが、実装からクラッタを取り除くことができます。 – mrAtari

+0

プリプロセッサディレクティブをテンプレートで置き換えることができます(他の人はそうすることはできないと言います)、そのような置き換えではテキストの総量が少なくなることはありません。だから、あなたはもっと大きくて不器用な何かを得るでしょう、それは読者のために驚くべき方法で働くでしょう。結局のところ、C++のコーダーは、PPディレクティブが何であるかを知っています。だから私はこれが勝利になるかどうか分からない。 –

答えて

1

UPDATE: 他の人が指摘している、と提供現実世界の例では、美しい方法でそれを示しているとして、それはプリプロセッサを取り除くすることはできませんとき、ケースがあります。特に、PPがabi::__cxa_demangleのようなプラットフォーム仕様をカバーしている場合。 しかし、私はテンプレートメタプログラミングには新しいので、このアプローチの利点と欠点を見いだすことが重要です。

これは私の独自の解決策ですが、残念ながら元のコードとほぼ同じ3倍です。これは主に、ソリューションに必要なヘルパーのためです。質問1について

Iは、ユーザ定義型にプリプロセッサ値をtranlateする列挙とInt2Typeマッパーを使用します。第2のステップでは、異なる部分が部分的に特殊化されたテンプレートに抽出される。

ここリファクタリングコード例(バージョン2)は次のとおり

#include <iostream> 
#include <cstring> 
#include <stdlib.h> 
#include <typeinfo> 


enum CompilerIds 
{ 
    ANY = 0, 
    MSC = 1, 
    GNUC = 2 
}; 

template <int v> 
struct Int2Type 
{ 
    const static int value= v; 
}; 

#ifdef __GNUC__ 
#include <cxxabi.h> 
typedef Int2Type<GNUC> CompilerType; 
#elif _MSC_VER 
namespace abi 
{ 
    char* __cxa_demangle(const char* name, int n, size_t* sz, int* status); 
} 
typedef Int2Type<MSC> CompilerType; 
#else 
typedef Int2Type<ANY> CompilerType; 
#endif 

template <int N> 
struct compiler_traits 
{ 
    static std::string demangle(std::string name) 
    { 

     return name; 
    } 
}; 

template <> 
struct compiler_traits<GNUC> 
{ 
    static std::string demangle(std::string name) 
    { 
     size_t sz; 
     int status; 
     char* ptr = abi::__cxa_demangle(name.c_str(), 0, &sz, &status); 
     std::string retName(ptr ? ptr : "", ptr ? strlen(ptr) : 0); 
     if(ptr){ free(ptr); } 
     std::string::size_type pos = retName.rfind("::"); 
     if(pos != std::string::npos) 
     { 
      retName = retName.substr(pos + 2); 
     } 
     return retName; 
    } 
}; 

template <> 
struct compiler_traits<MSC > 
{ 
    static std::string demangle(std::string name) 
    { 
     static const std::string struct_prefix("struct "); 
     static const std::string class_prefix("class "); 
     static const std::string ptr_postfix(" *"); 

     std::string::size_type 
     at = name.find(struct_prefix); 
     if(at != std::string::npos) { name.erase(at, struct_prefix.size()); } 
     at = name.find(class_prefix); 
     if(at != std::string::npos) { name.erase(at, class_prefix.size()); } 
     at = name.find(ptr_postfix); 
     if(at != std::string::npos) { name.erase(at, ptr_postfix.size()); } 

     return name; 
    } 
}; 

template <typename T, typename traits = compiler_traits<CompilerType::value> > 
struct demangle 
{ 
    static std::string Exec() 
    { 
     return traits::demangle(typeid(T).name()); 
    } 
}; 


int main() 
{ 
    std::cout << "\n mangled:" << typeid(Int2Type<GNUC>).name(); 
    std::cout << "\n demangled:" << demangle<Int2Type<GNUC> >::Exec() <<"\n"; 

    std::cout << "\n instatiate the msc version:" << compiler_traits<MSC>::demangle("struct Int2Type<2>") <<"\n"; 

    return 0; 
} 

GCC 4.9.2のために出力される。

mangled:8Int2TypeILi2EE 
demangled:Int2Type<2> 

instatiate the msc version:Int2Type<2> 

私はMSのための前方宣言とabi::__cxa_demangleをハックしなければなりませんでしたコンパイラ(これは決して実動コードでは行いません)。あなたは、MSシステム上のcompiler_traits<GNUC>のインスタンス化は失敗することがわかります。質問2については

原理的には、テンプレートとPPブロックを交換することが可能ですが、また、いくつかの重大な欠点を有することができます。

私はまだ、 "プラットフォームのもの"がなくても、特に#ifdef FEATURE_Aのような機能スイッチのようなものがあれば、それは努力する価値があります。テンプレートと

  • 拡張は非常に簡単で、OCP
  • を中断されませんそれはあなたが多くの#1のifdefの
  • Otherways到達不能コードが検証可能な
2

なっ持っている場合は特に、readabiltyを向上不可能です。 C++テンプレートは2フェーズルックアップ(Two phase name lookup for C++ templates - Why?)を使用しているため、関数テンプレート内で使用されるテンプレートパラメータに依存しない名前は、宣言の時点で使用可能でなければなりません。

GCCの実装では、名前がabi::__cxa_demangleであるため、その名前を指定しない実装ではコードを拒否する必要があります(2フェーズのルックアップが正しく実装されていないためである場合もあります)。What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation?

これを回避する唯一の方法は、abi::__cxa_demangleをプリプロセッサ#ifdefブロック内に含めることです。これは、元の実装をそのまま使用することを効果的に意味します。

+0

非常に良い点! – mrAtari

関連する問題