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": [] } ] }
追記
デフォルトのターミナル設定を書き換えている場合はそっちが実行されるのでexecutable
から指定する必要があります
私はMSYS2を使っているので、下記のようにします
{ // 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": true, "panel": "shared", "showReuseMessage": false, "clear": false }, "windows": { "options": { "shell": { "executable": "cmd.exe", "args": ["/d", "/c", "C:\\msys64\\msys2_shell.cmd -mingw64 -defterm -no-start -here -full-path"] } } }, "tasks": [ { "label": "build", "type": "shell", "command": "-c", "args": [ "mkdir -p build_msys_gcc; cd build_msys_gcc; cmake --build .; echo; echo '> Finished build task'" ], "group": { "kind": "build", "isDefault": true } } ] }
テンプレートで型情報を作る
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曲線の定義を思い出してみましょう
注目するのは、の部分です。例えば、が三次元ベクトル
で、ウェイトが設定されているとすると、
となります。
ここで、 とすれば、です。 xyz成分が一致しているので、これでなんとかなりそうです。
問題は、重みが変更されてしまうので、分母が変わってしまうことです。はすべて1ですので、正規化係数の分母は1になり消えます
よって、このときのNURBSは
あら偶然、結果のw成分に、重みの変更で消えた分母が見えています。これで結果の全体を除算して、xyz成分だけを取り出すと、となり、正しい結果を得れることがわかります
まとめ
有理Bスプライン曲線を、Bスプライン曲線用のアルゴリズムに適用するときは、次のような手順で一時的にn+1次のBスプライン曲線に変換する
- n+1次の新しいベクトルを用意し、先頭n次元にn次ベクトルの制御点座標をコピーする
- コピーしたベクトルに、重さを掛ける
- コピーしたベクトルのn+1次元要素に、重さをコピーする
- (アルゴリズムを適用する)
- 結果全体を、結果のn+1次元要素で割る
- 結果の先頭n次元を取り出して終わり
多くの場合、重み付きn次元座標はn+1次元ベクトルとして保存されているので、コードにするとかなり単純になります。 ベクトルの計算やコピーは、最適化がきくかどうかなどで変わってくるので、一番早い方法を検証する必要がありそうです