2017-08-09 23 views
1

現在、cpuまたはgpuのデータ処理をサポートするクラスを作成して、プリプロセッサ定義を使用して、含めるファイルを決定します(header)。私は両方のバリアントが必要になり同じクラスの2つの異なる実装をコンパイルする

IE

#ifdef CPU_work 
#include "cpu_backend.h" 
#endif 

#ifdef GPU_work 
#include "gpu_backend.h" 
#endif 

class Work { 
//Implementation dependant upon included header 
} 

しかし、そこに多分インスタンス。 ...とにかく私は....

namespace CPU { 
    #define CPU_work 
    //Generate implementation of WorkClass with cpu_backend.h 
} 
namespace GPU { 
     #define GPU_work 
     //Generate implementation of WorkClass with gpu_backend.h 
} 

をのようなものが行うことができていると私のようなものを経由たい実装決定そのため

CPU::Work cpuObject; 
GPU::Work gpuObject; 

はまた、任意の回避策に満足することでしょう。 多くのありがとうJJ。

+0

**私は多くの人に継承についてのコメントを与えるつもりです。しかし、これらの2つの異なるバージョンは、gpu/cpuへの単一行メソッド呼び出しとは別のコードを持っていますが、2つの別個のサブクラスを作成すれば十分ですが、30ページ以上のコードをコピー/ペーストする時間はかかりません。 –

+2

これは、実際には、宣言されたすべての関数が単一の名前空間と2つのcppファイル、すなわち共通インタフェースを実装する "cpu_work.cpp"と "gpu_work.cpp"と同一である単一のインタフェース "backend.h"彼ら自身の特別な方法。ビルド時に正しい実装ファイルをリンクします。 – user4581301

+0

これを行う正しい方法は、C++では継承です。親クラスから継承したサブクラスを作成し、読み始める方法を説明しているC++ブックの章を開きます。単一の基本クラスと、適切なCPUまたはGPU固有の機能を実装する2つのサブクラスがあります。 –

答えて

2

これは、テンプレートメソッドの設計を使用する場所です。基本クラスはCPUとGPUの両方に共通するすべてを実装し、抽象関数を使用します。

class Work { 
public: 
    void execute() { 
     // Do some initializing 
     foo(); 
     // Do some middle stuff 
     bar(); 
     // Do some final stuff 
    } 

private: 
    virtual void foo() = 0; 
    virtual void bar() = 0; 
} 

class CpuWork: public Work { 
    virtual void foo() { 
     // Do some CPU stuff 
    } 
    virtual void bar() { 
     // Do some more CPU stuff 
    } 
} 

class GpuWork: public Work { 
    virtual void foo() { 
     // Do some GPU stuff 
    } 
    virtual void bar() { 
     // Do some more GPU stuff 
    } 
} 

彼らは、基本クラスのプライベートメンバーであるので、それは抽象的なのでここでの事故によってあなたのベースクラスWorkを使用することはできませんし、誤って派生クラスfooまたはbarを呼び出すことはできません。

0

興味深い質問:あなたの目標が正しいと理解していれば、私はいくつかの解決策を提案できます。

Firstは、テンプレートの特殊化、テンプレートのデフォルト引数、(もちろん)いくつかのマクロを使用します。

はこれをチェックしてください:

// cpu_backend.h 
#define CPU_BACKEND 

class UseCPU; 

#ifndef GPU_BACKEND 
template<class Method = UseCPU> 
struct Backend; 
#endif 

template<> 
struct Backend<UseCPU> 
{ 
    char* Info() { return "CPU"; } 
}; 

// gpu_backend.h 
#define GPU_BACKEND 

class UseGPU; 

#ifndef CPU_BACKEND 
template<class Method = UseGPU> 
struct Backend; 
#endif 

template<> 
struct Backend<UseGPU> 
{ 
    char* Info() { return "GPU"; } 
}; 

// main.cpp 
// Try to swap comments on headers 
// and see how output changes 

#include "cpu_backend.h" 
//#include "gpu_backend.h" 

#include <iostream> 

template<class ... Method> 
struct Work 
{ 
    Work() 
    { 
     std::cout << "I use " << backend.Info() << std::endl; 
    } 

private: 
    Backend<Method ...> backend; 
}; 

int main() 
{ 
    Work<> work; 
    // Uncomment these two while including both headers 
    //Work<UseCPU> cpuWork; 
    //Work<UseGPU> gpuWork; 
    return 0; 
} 

をあなたはMSVCを使用している場合は、#define#ifndefを排除する上記の例を簡略化することができます。

トリック: MSVC(2017多分それ以前のバージョン)、彼らは で同じコンパイル単位を満たしている場合だけで2番目の宣言を無視して、マクロが脱穀することを省略することができ、このような:

template<class Method = UseCPU> 
struct Backend; 
template<class Method = UseGPU> 
struct Backend; 

BUTこれは標準ではありません。標準では、デフォルトのテンプレート引数を2回指定することはできません。あなたは両方のヘッダを含む場合

  • 、誰かがまだ あなたが含まれる第1ヘッダで指定されたバックエンドを使用しますどのWork<>を言うことができます。

一方、この解決策はいくつかの欠点があります。 しかし、コンパイラがこの状況で明示的に バックエンドタイプを指定した方が良い場合は、 はヘッダ組み込み順に依存しています( マクロにはあてはまります)。

  • また、それは両方のバックエンドが同じAPIを持っていることを前提として(のような私の場合はInfo() )それらのため

  • 考えられる解決:

    • 私はそれが可能であると確信しています ヘッダーが含まれていて明示的なバックエンドが指定されていない場合、コンパイラにエラーが発生しますが、 にはおそらくより多くのプリプロセッサのものが含まれています。

    • あなたのバックエンドは異なるAPIを持っている場合は、あなたは、このようなクールな機能へのアクセス を持っている場合(好ましくは)必要に応じて、またはC++ 17 if constexpr(std::is_same<Method, UseCPU>()::value)を使用し、いくつかの #ifdefを挿入することができます:)

    +0

    間違いなくクールなトリックですが、私はそれを標準に保つことを好むでしょう。これはバグを求めているようだ。 –

    +0

    申し訳ありませんが、おそらく私はそれを間違えました。私が提供したコードは標準です(私が想定しているC++ 11)。 Visual Studioの "トリック"は標準ではありませんが、 '#ifndef GPU_BACKEND'や友人がいなくても同じことができます。それがあなたが「標準」の意味である場合) – WindyFields

    関連する問題