茂加部珈琲店

主にtech関連のメモ置き場です

if constexprを失敗させる

if constexprを与えられた型によって失敗させる場合は、
型に依存したパラメータをstatic_assertに渡してやれば良いみたいです。

saka1_pさんの記事によれば、

template <typename T>
constexpr bool false_v = false;

のような定数を定義して、

  // if constexprの内部で
  static_assert(false_v<T>, "invalid type T");

のようにするらしいです。

個人的には

  static_assert(!sizeof(T), "invalid type T");

が好みです。

VSCodeでcmakeを走らせる

コマンド一つでcmakeやctestを走らせたかったのです。

VSCodeではtask.jsonを使って簡単にできるみたいです.
WindowsではデフォルトのコンパイラVC++なので、MinGWを指定してあります
mingw-64でも動きました。

Ctrl+Shift+B で走ります

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "presentation": {
    "echo": true,
    "reveal": "always",
    "focus": false,
    "panel": "new"
  },
  "tasks": [
    {
      "label": "build",
      "type": "shell",
      "windows": {
        "command": "",
        "args": [
          "mkdir build; cd build; cmake -G 'MinGW Makefiles' ..; cmake --build .; ctest -V -C Debug"
        ]
       },
      "linux": {
        "command": "sh",
        "args": [
          "mkdir build; cd build; cmake ..; cmake --build .; ctest -V"
        ]
       },
       "problemMatcher": []
    }
  ]
}

テンプレートで型情報を作る

C++勉強ネタです

テンプレートを使って、関数型言語の型情報を生成してみます(やっつけなのでバグがあるかも)

値型はValue, λ抽象はArrowになっています。 とりあえずValueには設定された文字列を格納し、Arrowは単にリストを作ってるだけです。

型Tが関数型を持つ場合は、std::tupleシグネチャを格納しておきます

静的変数のアドレスはconstexprとして、初期化前に取得することができます。
依存関係のある静的変数を初期化するのって大変ですよね…
staticローカル変数を使っても良いのですが、そうすると初期化が再帰してUBになるかもです

#include <tuple>
#include <iostream>
#include <variant>
#include <cassert>

template <class T>
struct traits;

// value type
struct Value;
// arrow type
struct Arrow;
// union
using Type = std::variant<Arrow,Value>;

struct Arrow{
  Type* captured;
  Type* returns;
};
struct Value{
  const char* name;
};

template <class T>
struct value_type{
  static Type type;
};

// arrow_type impl
template <class T, class ...Ts>
struct arrow_type_{
  static Type type;
};
// arrow_type impl
template <class T1, class T2>
struct arrow_type_<T1,T2>{
  static Type type;
};

template <class T, class ...Ts>
struct arrow_type;

template <class T, class ...Ts>
struct arrow_type<T, std::tuple<Ts...>>{
  static constexpr Type* type = &arrow_type_<Ts...>::type;
};

template <class T, class = void>
struct object_type {
  static constexpr Type* type = arrow_type<T,typename T::type>::type;
};

template <class T>
struct object_type<T, std::void_t<decltype(traits<T>::name)>>{
  static constexpr Type* type = &value_type<T>::type;
};

template <class T>
Type value_type<T>::type {Value{traits<T>::name}};

template <class T, class ...Ts>
Type arrow_type_<T,Ts...>::type {Arrow{ object_type<T>::type, &arrow_type<Ts...>::type }};

template <class T1, class T2>
Type arrow_type_<T1,T2>::type {Arrow{object_type<T1>::type, object_type<T2>::type}};

// define int type
template <>
struct traits<int>{
  static constexpr const char* name = "int";
};
int main () {    
  struct foo{
    // basic closure type (int->int)
    using type = std::tuple<int,int>;
  };
  struct bar{
    // recursive closure type (bar->int) 
    using type = std::tuple<bar,int>;
  };
  // value
  auto i = object_type<int>::type;
  std::cout << std::get<Value>(*i).name << std::endl; // int
  // closure
  auto f = object_type<foo>::type;
  assert(std::get<Arrow>(*f).captured == i); 
  assert(std::get<Arrow>(*f).returns == i); 
  // recursive
  auto b = object_type<bar>::type;
  assert(b == std::get<Arrow>(*b).captured);
  return 0;
}

可変長テンプレートでunion

C++勉強ネタが続きます

templateを作って直和型もどきを作りたいときがあります。
これはすでにBoost Libraryで実現されていて、boost::variantとして利用できます。
また、C++1zでは、これが標準に追加されます。

今回は、可変長テンプレートを使ってこれを実装してみたいと思い立ち書いてます。 boost::variantはインデックス用の数字を保存しますが、今回は値だけを格納することにします。

とりあえず、データ構造はこんな感じでしょうか
U[3]U.nxt.nxt.nxt.vになるみたいなイメージです

template <size_t N, class Head, class ...Tail>
union U {
  Head v;
  U<N-1 ,Tail...> nxt;
};

template <class Head,class ...Tail>
union U<0,Head,Tail...> {
  Head v;
};

これを初期化するクラスを作っておきます

template <class ...Args>
class Union{
  U<sizeof...(Args)-1,Args...> u;
};

中身を取り出したいので、

auto v3 = get<3>(u); 

みたいにかけるようにしたいですね

template <size_t N>
struct _getter {
  template <size_t U_N, class U_Head, class... U_Tail>
  static auto get(const U<U_N,U_Head,U_Tail...>& u){
    return _getter<N-1>::get(u.nxt);
  }
};

template <>
struct _getter<0>{
  template <size_t U_N, class U_Head, class... U_Tail>
  static auto get(const U<U_N,U_Head,U_Tail...>& u){
    return u.v;
  }
};

template <size_t N, class...Args>
auto get(const Union<Args...>& u){
  return _getter<N>::get(u.u);
}

うーん…

int main(){
  union _u {
    char c;
    double d;
    int i;
  };  
  Union<char ,double ,int> uni;
  variant<char, double ,int> var;
  auto v0 = get<0>(uni); 
  
  cout << sizeof(v0) << endl;   // 1
  cout << sizeof(_u) << endl;   // 8
  cout << sizeof(uni) << endl;  // 8
  cout << sizeof(var) << endl;  // 16
}

unionと同サイズになることがわかりますね。
独自の型を実装していて、セレクタが不要な場合などに使えそう。

c++17のaggregate initialization

c++では、十分に「しょぼい」クラスに対して、aggregate initializationが使えるみたいです。
aggregate initializationはリスト初期化の一種で、
色々できることがありますが、例をあげると

struct Foo {
  int i;
  float f;
};

というクラスは、

  Foo f = { 4, 2. };

のようにして初期化できます。便利ですね。 c++17からは、これが更に拡張されるみたいです

例えば、

struct Header {
  long l;
};

struct Foo : Header {
  int i;
};

このいかにもC言語風の構造体は、

  Foo f = { {4}, 2 };

もしくは

  Foo f = { 4, 2 };

のように初期化できます。良いですね!

デフォルトテンプレート引数を持つクラスのstaticメンバを初期化する

template <class T, class = int>
class Foo{
  static int i;
};

template <class T>
int Foo<T>::i = 42;

これは動きません。

template <class T, class = int>
class Foo{
  static int i;
};

template <class T, class E>
int Foo<T,E>::i = 42;

こうやって全部きっちり書く必要があるみたいです

有理Bスプライン曲線の非有理化

今回はNURBS曲線の小ネタです
非有理化と言っていますが、正しくは計算過程で一時的に次元を上げることができるよ、という話です
これを利用すれば、有理化を考慮していないアルゴリズム(Bスプライン用の計算など)に、NURBSを適用することができます

有理化されたn次Bスプライン曲線を、非有理(すべての重さが1)のn+1次Bスプライン曲線に変換する

n次の有理Bスプライン曲線は、簡単にn+1次の非有理曲線に変換することができます。   NURBS曲線の定義を思い出してみましょう

{ \displaystyle
C(u) = \frac{\sum_{i=0}^{k-1} N_{i,n}(u)w_iP_i } {\sum_{i=0}^{k-1} N_{i,n}(u)w_i}
}

注目するのは、{ w_iP_i}の部分です。例えば、{P_i}が三次元ベクトル {\begin{pmatrix}
 x_i \\
 y_i \\
 z_i
\end{pmatrix}} で、ウェイト{w_i}が設定されているとすると、
{w_iP_i=\begin{pmatrix} x_iw_i \\ y_iw_i \\ z_iw_i \end{pmatrix}}となります。
ここで、{P'_i = \begin{pmatrix} x_iw_i \\ y_iw_i \\ z_iw_i \\ w_i \end{pmatrix}, w'_i = 1} とすれば、{w'_iP'_i=\begin{pmatrix} x_iw_i \\ y_iw_i \\ z_iw_i \\ w_i \end{pmatrix}}です。 xyz成分が一致しているので、これでなんとかなりそうです。
問題は、重み{w}が変更されてしまうので、分母が変わってしまうことです。{w'_i}はすべて1ですので、正規化係数の分母は1になり消えます
よって、このときのNURBSは { \displaystyle
C(u) = \sum_{i=0}^{k-1} N_{i,n}(u)w'_iP'_i
= \begin{pmatrix}
\sum_{i=0}^{k-1} N_{i,n}(u)x_iw_i \\
\sum_{i=0}^{k-1} N_{i,n}(u)y_iw_i \\
\sum_{i=0}^{k-1} N_{i,n}(u)z_iw_i \\
\sum_{i=0}^{k-1} N_{i,n}(u)w_i
\end{pmatrix}
}

あら偶然、結果のw成分に、重みの変更で消えた分母が見えています。これで結果の全体を除算して、xyz成分だけを取り出すと、{\frac{\sum_{i=0}^{k-1} N_{i,n}(u)w_iP_i } {\sum_{i=0}^{k-1} N_{i,n}(u)w_i}}となり、正しい結果を得れることがわかります

まとめ

有理Bスプライン曲線を、Bスプライン曲線用のアルゴリズムに適用するときは、次のような手順で一時的にn+1次のBスプライン曲線に変換する

  • n+1次の新しいベクトルを用意し、先頭n次元にn次ベクトルの制御点座標をコピーする
  • コピーしたベクトルに、重さ{w}を掛ける
  • コピーしたベクトルのn+1次元要素に、重さ{w}をコピーする
  • (アルゴリズムを適用する)
  • 結果全体を、結果のn+1次元要素で割る
  • 結果の先頭n次元を取り出して終わり

多くの場合、重み付きn次元座標はn+1次元ベクトルとして保存されているので、コードにするとかなり単純になります。 ベクトルの計算やコピーは、最適化がきくかどうかなどで変わってくるので、一番早い方法を検証する必要がありそうです