ゆとりーなの日記

日記的な事を書いて行くと思はれる

FVFを型で自動計算

DirectXで使うFVFを型で計算してしまおうという無謀な計画が実ったのでメモしておきます。
まず、tagとして次のような空の構造体を定義しておきます。雰囲気イテレータとかで使うあれを真似してみました。

struct dimension2_drawable_tag {}; // D3DFVF_XYZRHW
struct dimension3_drawable_tag {}; // D3DFVF_XYZ
struct normal_drawable_tag {};     // D3DFVF_NORMAL
struct diffuse_drawable_tag {};    // D3DFVF_DIFFUSE
struct speculer_drawable_tag {};   // D3DFVF_SPEQULER
struct tex1_drawable_tag {};       // D3DFVF_TEX1
struct tex2_drawable_tag {};       // D3DFVF_TEX2

続いてこれらの型と実際のFVF値を関連付けたマップを用意します。今回はboost::mpl::mapを使いましたが正直mapにする意味があったかどうかは微妙です。

#include <cstdint>
#include <boost/mpl/map.hpp>
#include <boost/mpl/integral_c.hpp>
#include <d3d9.h>

typedef boost::mpl::map<
    boost::mpl::pair<dimension2_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_XYZRHW>>,
    boost::mpl::pair<dimension3_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_XYZ>>,
    boost::mpl::pair<normal_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_NORMAL>>,
    boost::mpl::pair<diffuse_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_DIFFUSE>>,
    boost::mpl::pair<speculer_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_SPECULAR>>,
    boost::mpl::pair<tex1_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_TEX1>>,
    boost::mpl::pair<tex2_drawable_tag, boost::mpl::integral_c<std::uint32_t, D3DFVF_TEX2>>> fvf_map;

更に実際に必要な型のリストを作成するために空のベクトルを用意します。ここでマップを用意しないのはマップだとpush_back関連がこけるからです。

#include <boost/mpl/vector.hpp>

typedef boost::mpl::vector<> empty_fvf_list;

最後に計算部分です。

#include <type_traits>
#include <boost/mpl/copy_if.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/lambda.hpp>

// マップから来るペアの一番目とタグが継承関係にあるかを調べる
template <typename Lhs, typename Rhs>
struct fvf_is_base_of {
  typedef bool value_type;
  static const value_type value = std::is_base_of<typename Lhs::first, Rhs>::value;
};

// 前の値とペアの二番目を足す
template <typename Lhs, typename Rhs>
struct bitor_fvf_type {
  typedef std::uint32_t value_type;
  static const value_type value = typename Lhs::value | typename Rhs::second::value;
};

template <typename Drawable>
struct fvf_traits {
  // 必要な要素だけを取り出したベクトルを作成
  typedef typename boost::mpl::copy_if<fvf_map, 
                                       fvf_is_base_of<boost::mpl::_1, typename Drawable::drawable_tag>,
                                       boost::mpl::back_inserter<empty_fvf_list>>::type fvf_list;
  // 作成したベクトルの要素のFVF値を只管足していく
  typedef typename boost::mpl::fold<fvf_list, 
                                    boost::mpl::integral_c<std::uint32_t, 0>, 
                                    bitor_fvf_type<boost::mpl::_1, boost::mpl::_2>>::type fvf;
};

次のように使えます。

struct tag : public dimension2_drawable_tag, public diffuse_drawable_tag {};

struct box {
  typedef tag drawable_tag;
};

int main() {
  const std::uint32_t fvf = fvf_traits<box>::fvf::value; // D3DFVF_XYZRHW | D3DFVF_DIFFUSE
  return 0;
}