template class の実装を別ファイルに分離


導入

普通の CPU 用 (C++) と GPU 用 (CUDA) のコードを宣言(.hpp)とか使い方は共通にして、実装をわけたい。kernel関数とか thrust::device_vector とかは CUDA のコード(*.cu)にしか書けない。

やり方

a.hpp
#pragma once
#include <memory>

template <typename T>
class A {
public:
    A();
    virtual ~A();
    void where();
private:
    A(const A&);
    const A& operator=(const A&);
    struct Impl;
    Impl* _impl;
};

// 実体化したい型Tを列挙、ないものはリンク失敗
template class A<float>;
template class A<double>;
a_common.hpp
// CPU, GPU 実装で共通の部分
#include <a.hpp>

template <typename T>
A<T>::A() : _impl(new Impl) {}

template <typename T>
A<T>::~A() { delete _impl; }

template <typename T>
void A<T>::where() { _impl->msg(); }
a.cpp
// CPU 用の実装
#include <iostream>
#include "a_common.hpp"

template <typename T>
struct A<T>::Impl {
    void msg() {
        std::cout << "from cpp" << std::endl;
    }
};
a.cu
// GPU 用の実装
#include <iostream>
#include "a_common.hpp"

template <typename T>
struct A<T>::Impl {
    void msg() {
        std::cout << "from cuda" << std::endl;
    }
};

ビルド方法とかは CMake のプロジェクトあげましたので見てください。

https://github.com/ShigekiKarita/CMakeExampleCUDA

実行結果.txt
$ ./cmk.sh
$ ./build/g++/release/test/test_a_cpu
from cpp
$ ./build/g++/release/test/test_a_gpu
from cuda

理由

ググるとこういうのがでます。前者や後者のメソッド 2, 3 はGPU用の .cu ファイルを不適ですが、。

後者のメソッド 1 は使えそうですが、最適化 "-O" オプションによってリンクが通らなくなりました。

これはいけるんですが、全メソッド書くのが面倒ですので宣言 class だけで十分、メンバもCPU/GPUで変えたいので pimpl にしました。

補足

今年リリースされた CUDA 7.0 ではついに C++11 が利用可能になりました。つまり上記 pimpl などの生ポインタを使う部分をスマートポインタで置き換え可能になり、オーバーヘッドなしでメモリ管理でき、記述量が減ります (他にも thrust で頻出のファンクタもラムダにできますね)。CMake では次のように C++11 を nvcc に設定します。

CMakeLists.txt
set(CUDA_NVCC_FLAGS "-std=c++11")