DxLibEx
DxLibEx copied to clipboard
sizeクラスを作る
#2 のx,yじゃなくてwidth, height版
とりあえず作ったけど、どうやって相互変換しよう。 OpenCVは双方のコンストラクタに追加するやり方にしているけれど。
colorの方ではoperatorを使ってますが、統一したほうが良いと思います。 explicitはつけたほうが良いと思います。
ということは双方のヘッダーで互いを読み込み合うように書かないといけないのか・・・。
クラスの宣言と関数の実装を別々のヘッダーに分けたほうが実装しやすいと思いますがどうでしょう?
別々にしようとしたら、再定義とか定義がないとか怒られたのでこんな感じにゴリ押ししてみました。 http://homepage2.nifty.com/well/Header-Mutual-Inclusion-Template.html 一応これと同じやり方です。
template <typename T1, typename T2>
auto operator +(const point_c<T1>& l, const size_c<T2>& r) DXLE_NOEXCEPT_OR_NOTHROW -> point_c<decltype(l.x + r.height)>
って書くと、IntelliSenseがちゃんと認識してくれないみたいで・・・どうしよう。(コンパイルは通る)
二項演算子+=と-=なんですが、左右の内部型が違う場合ってどうしましょう。
IntelliSenseがちゃんと認識してくれない
確認しましたが、operator*も認識されてないですね...。どうして<error-type>になるんだろう。
二項演算子+=と-=なんですが、左右の内部型が違う場合ってどうしましょう。
戻り値を左辺に合わせるで良いと思います。
戻り値を左辺に合わせるで良いと思います。
キャストしますか?暗黙の型変換のみにしますか?
暗黙の型変換のみで良いと思います。 ~~decltypeを使ってSFINAEではじくのが良いと思います。~~ SFINAEではじくのと、はじかずにエラーになるのとどっちがわかりやすいんでしょうか? 前者はIntelliSenseでエラーがでるもののエラーメッセージが長くなる。 後者はエラーメッセージは短いものの、IntelliSenseでエラーが出ない上、エラー個所がライブラリの中になる。
SFINAEではじく
どう書けばいいのか思いつかないんですが・・・
SFINAEではじく
こんな感じです。
template<typename>
struct ignore : std::true_type{};
template<typename T>
//戻り値intの関数
auto add(int a, T b)->std::enable_if_t<ignore<decltype(a + b)>::value, int>
{
return a + b;
}
int main()
{
add(0, 0);//OK
add(0, nullptr);//エラー
return 0;
}
template <typename T1, typename T2>
auto operator +=(point_c<T1>& l, const point_c<T2>& r) DXLE_NOEXCEPT_OR_NOTHROW -> point_c<enable_if_t<std::is_same<decltype(l.x + r.x), T1>::value, T1>>&
{
l.x += r.x;
l.y += r.y;
return l;
}
こんなかんじに書き換えて
int foo() {
dxle::pointi p1 = { 2, 5 };
dxle::pointu8i p2 = { 3, 5 };
p1 += p2;
}
こう呼ぶコードを書いたところ、Clang with Microsoft CodeGenではとおり、
1>------ すべてのリビルド開始: プロジェクト:calc_coordinate, 構成:clang_Debug x64 ------
1> calc_coordinate.cpp
1> calc_coordinate.vcxproj -> C:\Users\yumetodo\Documents\git\DxLibEx\samples\calc_coordinate\x64\clang_Debug\calc_coordinate.exe
1> calc_coordinate.vcxproj -> C:\Users\yumetodo\Documents\git\DxLibEx\samples\calc_coordinate\x64\clang_Debug\calc_coordinate.pdb (Full PDB)
========== すべてリビルド: 1 正常終了、0 失敗、0 スキップ ==========
MSVCでは
1>------ ビルド開始: プロジェクト:calc_coordinate, 構成:Debug x64 ------
1> calc_coordinate.cpp
1>c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): error C2893: 関数テンプレート 'dxle::point_c<dxle::enable_if<B,T1>::type,nullptr> &dxle::operator +=(dxle::point_c<T,nullptr> &,const dxle::point_c<PointType,nullptr> &) noexcept' の特定に失敗しました
1> c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): note: 次のテンプレート引数で:
1> c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): note: 'T1=int')
1> c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): note: 'T2=uint8_t')
1>c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): error C2784: 'dxle::point3d_c<T,nullptr> &dxle::operator +=(dxle::point3d_c<T,nullptr> &,const dxle::point3d_c<T,nullptr> &) noexcept': テンプレート 引数を 'dxle::point3d_c<T,nullptr> &' に対して 'dxle::pointi' から減少できませんでした
1> c:\users\yumetodo\documents\git\dxlibex\dxlibex\basic_types\point3d.hpp(272): note: 'dxle::operator +=' の宣言を確認してください
1>c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): error C2784: 'dxle::size_c<T,nullptr> &dxle::operator +=(dxle::size_c<T,nullptr> &,const dxle::size_c<T,nullptr> &) noexcept': テンプレート 引数を 'dxle::size_c<T,nullptr> &' に対して 'dxle::pointi' から減少できませんでした
1> c:\users\yumetodo\documents\git\dxlibex\dxlibex\basic_types\size.hpp(274): note: 'dxle::operator +=' の宣言を確認してください
1>c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): error C2784: 'dxle::point_c<T,nullptr> &dxle::operator +=(dxle::point_c<T,nullptr> &,const dxle::size_c<T,nullptr> &) noexcept': テンプレート 引数を 'const dxle::size_c<T,nullptr> &' に対して 'dxle::pointu8i' から減少できませんでした
1> c:\users\yumetodo\documents\git\dxlibex\dxlibex\basic_types.hpp(29): note: 'dxle::operator +=' の宣言を確認してください
1>c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): error C2784: 'dxle::size_c<T,nullptr> &dxle::operator +=(dxle::size_c<T,nullptr> &,const dxle::point_c<T,nullptr> &) noexcept': テンプレート 引数を 'dxle::size_c<T,nullptr> &' に対して 'dxle::pointi' から減少できませんでした
1> c:\users\yumetodo\documents\git\dxlibex\dxlibex\basic_types.hpp(48): note: 'dxle::operator +=' の宣言を確認してください
1>c:\users\yumetodo\documents\git\dxlibex\samples\calc_coordinate\calc_coordinate\calc_coordinate.cpp(12): error C2676: 二項演算子 '+=': 'dxle::pointi' は、この演算子または定義済の演算子に適切な型への変換の定義を行いません。(新しい動作; ヘルプを参照)
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
エラーになった。どゆこと?
mingwのgccとclangも試しました。問題なしです。
yumetodo@yumetodo-PC MINGW64 /c/Users/yumetodo/Documents/git/DxLibEx/sample s/calc_coordinate
$ make Debug CXX=clang++
clang++ calc_coordinate/calc_coordinate.cpp -o calc_coordinate.exe -g -O0 -Wall -std=c++14 -fvisibility=hidden -I "../../"
yumetodo@yumetodo-PC MINGW64 /c/Users/yumetodo/Documents/git/DxLibEx/samples/calc_coordinate
$ touch ./calc_coordinate/calc_coordinate.cpp
yumetodo@yumetodo-PC MINGW64 /c/Users/yumetodo/Documents/git/DxLibEx/samples/calc_coordinate
$ make Debug
g++ calc_coordinate/calc_coordinate.cpp -o calc_coordinate.exe -g -O0 -Wall -std=c++14 -fvisibility=hidden -I "../../" --input-charset=utf-8 -fexec-charset=CP932
別ブランチに上の状態を上げました↓
- test commitの状態だとpointu8i系の+=が実質使用不可になってしまう。
- ignore形式にしたらコンパイルが通った
- なぜtest commitのだとだめなのかという疑問は残るけれども、とりあえずignore形式の物を使うで良いのではないか
というわけでcommitしようと思ったのですが、作業データを消してしまったので、commitは後でにします。
@Nagarei
~~ignoreクラス作りたくないぁ、と思ったらそういえばstd::declvalあったなぁということで、もう少し粘ってみます。~~
template <typename T1, typename T2>
auto operator +=(point_c<T1>& l, const point_c<T2>& r) DXLE_NOEXCEPT_OR_NOTHROW->point_c < enable_if_t < std::is_same<decltype(std::declval<T1>() * std::declval<T2>()), T1>::value, T1 >> &
{
l.x += r.x;
l.y += r.y;
return l;
}
だめでした。なんでや。
試したらこれで行けました。
->enable_if_t < std::is_same<decltype(l.width + r.width), T1>::value, point_c<T1>&>
ですが、これだとpointu8i系の+=が実質使用不可になってしまうのですが...。
void foo2() {
dxle::pointu8i p3 = { 2, 5 };
dxle::pointu8i p4 = { 3, 5 };
p3 += p4;
}
だとしても、uint8_t + uint8_t => int なのでis_sameがfalseになってしまいます。
そうなんですよねぇ・・・。 https://github.com/Nagarei/DxLibEx/commit/f3de959467fd39d75070e4ab620daff3657d0bd4 これの場合安定と信頼のIntelliSenseバグりが発生するし・・・。
整数型同士はsizeofみて、ほかは自力で判定書いて、これようのメタテンプレートつくります?
メタテンプレートを新しく書いてもIntelliSenseバグりが治る確証がないんですよね...。 ただ、+=に限って言えば、赤線が出なければIntelliSenseがおかしくても気にならないとは思いますが。
それっぽいもの作りましたが http://melpon.org/wandbox/permlink/ICHvO7j5mROuFOrJ 安定と信頼のIntelliSenseバグりからの、constexpr関数使ってるからVS2013で使えないのでダメですね。
もうSFINAEするのを諦めませんか?
SFINAEがなくてもエラーになるのでSFINAEを外しても良いと思います。
外してみました。
うわぁ、江添さん、記事にしちゃった。びっくり。 yumetodoが本当に欲しかったもの | 本の虫 http://cpplover.blogspot.jp/2016/01/yumetodo.html
という感じでNarrowing Conversionsを禁止するようにしてみたbranchを作りましたが、どうでしょう?
そういえば
const dxle::size_c<std::uint8_t> s1 = { 3, 7 };
const auto s2 = s1 * 2;//dxle::size_c<int>
これは望ましい挙動なのだろうか。
Narrowing Conversionsを禁止
IntelliSenseがちゃんと利いてますね。良かった。これで良いと思います。
挙動
std::uint8_t v1 =3;
const auto v2 = v1 * 2;//int
これに違和感を覚えるかどうかの話になってきますね...。
IntelliSenseがちゃんと利いてますね。良かった。これで良いと思います。
というわけでpullrequest出しました
そういえば内部型のちがうクラス同士のキャストもNarrowing Conversionsではないならexplicit外すとかできるのだろうか、できるならした方がいいけれど。
Narrowing Conversionsではないならexplicit外す
SFINAE使えばできるのですが、VS2013が訳が分からないエラーを出すので現在原因調査中です。 現在試行錯誤中のコード
template < typename Tp2_, enable_if_t<is_representable<T, Tp2_>::value, std::nullptr_t> = nullptr>
operator point_c<Tp2_>() const DXLE_NOEXCEPT_OR_NOTHROW
{
return{ static_cast<Tp2_>(this->x), static_cast<Tp2_>(this->y) };
}
template < typename Tp2_, enable_if_t<!is_representable<T, Tp2_>::value, std::nullptr_t> = nullptr, std::nullptr_t = nullptr>
explicit operator point_c<Tp2_>() const DXLE_NOEXCEPT_OR_NOTHROW
{
return{ static_cast<Tp2_>(this->x), static_cast<Tp2_>(this->y) };
}
エラー
error C2440: 'static_cast' : 'const dxle::pointf' から 'dxle::pointu8i' に変換できません。
1> class 'dxle::point_c<uint8_t,default_valueAttribute>' のコンストラクターが 'explicit' と宣言されています。
なぜ「'explicit' と宣言されてい」るがためにstatic_castに失敗せねばならんのか。
ちなみに、VS2013 November CTPだとどうなりますか? (2015入れてから2013入れるのは難儀で、かといって2015アンインストールしてからというのも難儀なのです、すみません)
VS2013 November CTP
かなりの数のエラーが出ました...。分類が雑な状態ですが、とりあえず置いときます。
1>------ ビルド開始: プロジェクト:calc_coordinate, 構成:Debug Win32 ------
1> calc_coordinate.cpp
1>c:\...\dxlibex\basic_types\arithmetic_t.hpp(15): error C2440: 'default argument' : cannot convert from 'nullptr' to 'enable_if<std::is_arithmetic<_Ty>::value,std::nullptr_t>::type'
1> 'enable_if<std::is_arithmetic<_Ty>::value,std::nullptr_t>::type' may be a value type at runtime: consider using 'enable_if<std::is_arithmetic<_Ty>::value,std::nullptr_t>::type()' instead
1>c:\...\dxlibex\algorithm\safe_dist.hpp(14): error C2440: 'default argument' : cannot convert from 'nullptr' to 'enable_if<std::is_arithmetic<_Ty>::value&&std::is_arithmetic<T2>::value,std::nullptr_t>::type'
1> 'enable_if<std::is_arithmetic<_Ty>::value&&std::is_arithmetic<T2>::value,std::nullptr_t>::type' may be a value type at runtime: consider using 'enable_if<std::is_arithmetic<_Ty>::value&&std::is_arithmetic<T2>::value,std::nullptr_t>::type()' instead
1>c:\...\dxlibex\basic_types\point2d.hpp(70): error C2440: 'default argument' : cannot convert from 'nullptr' to 'enable_if<std::is_arithmetic<_Ty>::value,std::nullptr_t>::type'
1> 'enable_if<std::is_arithmetic<_Ty>::value,std::nullptr_t>::type' may be a value type at runtime: consider using 'enable_if<std::is_arithmetic<_Ty>::value,std::nullptr_t>::type()' instead
1>c:\...\dxlibex\basic_types\point2d.hpp(79): error C2976: 'dxle::point_c' : too few template arguments
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
1> c:\...\dxlibex\basic_types\point2d.hpp(127) : see reference to class template instantiation 'dxle::point_c<T,__formal>' being compiled
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
...
1>c:\...\dxlibex\basic_types\point2d.hpp(117): error C2972: 'dxle::size_c' : template parameter 'unnamed-parameter' : the type of non-type argument is invalid
1> c:\...\dxlibex\basic_types\point2d.hpp(27) : see declaration of 'dxle::size_c'
1>c:\...\dxlibex\basic_types\point2d.hpp(117): error C2027: use of undefined type 'dxle::size_c'
1> c:\...\dxlibex\basic_types\point2d.hpp(27) : see declaration of 'dxle::size_c'
1>c:\...\dxlibex\basic_types\point2d.hpp(139): error C2976: 'dxle::point_c' : too few template arguments
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
1>c:\...\dxlibex\basic_types\point2d.hpp(152): error C2976: 'dxle::point_c' : too few template arguments
...
1>c:\...\dxlibex\basic_types\point2d.hpp(250): error C2976: 'dxle::point_c' : too few template arguments
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
...
1>c:\...\dxlibex\basic_types\point2d.hpp(291): error C2440: 'default argument' : cannot convert from 'nullptr' to 'enable_if<dxle::is_representable<T2,T1>::value,std::nullptr_t>::type'
1> 'enable_if<dxle::is_representable<T2,T1>::value,std::nullptr_t>::type' may be a value type at runtime: consider using 'enable_if<dxle::is_representable<T2,T1>::value,std::nullptr_t>::type()' instead
1>c:\...\dxlibex\basic_types\point2d.hpp(291): error C2976: 'dxle::point_c' : too few template arguments
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
1> c:\...\dxlibex\basic_types\point2d.hpp(71) : see declaration of 'dxle::point_c'
...
========== ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ ==========
うわぁ・・・。 頑張って2013入れ直しますか・・・。
value_typeが同じpoint_cへの暗黙の変換を許可したほうが良いと思いますが、どう思いますか?
オーバーロード解決周りが不安ですね・・・。大丈夫なら許可して大丈夫だと思いますが