DxLibEx icon indicating copy to clipboard operation
DxLibEx copied to clipboard

sizeクラスを作る

Open yumetodo opened this issue 9 years ago • 32 comments

#2 のx,yじゃなくてwidth, height版

yumetodo avatar Jan 05 '16 08:01 yumetodo

とりあえず作ったけど、どうやって相互変換しよう。 OpenCVは双方のコンストラクタに追加するやり方にしているけれど。

yumetodo avatar Jan 05 '16 10:01 yumetodo

colorの方ではoperatorを使ってますが、統一したほうが良いと思います。 explicitはつけたほうが良いと思います。

Nagarei avatar Jan 05 '16 11:01 Nagarei

ということは双方のヘッダーで互いを読み込み合うように書かないといけないのか・・・。

yumetodo avatar Jan 05 '16 12:01 yumetodo

クラスの宣言と関数の実装を別々のヘッダーに分けたほうが実装しやすいと思いますがどうでしょう?

Nagarei avatar Jan 05 '16 12:01 Nagarei

別々にしようとしたら、再定義とか定義がないとか怒られたのでこんな感じにゴリ押ししてみました。 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がちゃんと認識してくれないみたいで・・・どうしよう。(コンパイルは通る)

二項演算子+=と-=なんですが、左右の内部型が違う場合ってどうしましょう。

yumetodo avatar Jan 05 '16 13:01 yumetodo

IntelliSenseがちゃんと認識してくれない

確認しましたが、operator*も認識されてないですね...。どうして<error-type>になるんだろう。

二項演算子+=と-=なんですが、左右の内部型が違う場合ってどうしましょう。

戻り値を左辺に合わせるで良いと思います。

Nagarei avatar Jan 06 '16 02:01 Nagarei

戻り値を左辺に合わせるで良いと思います。

キャストしますか?暗黙の型変換のみにしますか?

yumetodo avatar Jan 06 '16 03:01 yumetodo

暗黙の型変換のみで良いと思います。 ~~decltypeを使ってSFINAEではじくのが良いと思います。~~ SFINAEではじくのと、はじかずにエラーになるのとどっちがわかりやすいんでしょうか? 前者はIntelliSenseでエラーがでるもののエラーメッセージが長くなる。 後者はエラーメッセージは短いものの、IntelliSenseでエラーが出ない上、エラー個所がライブラリの中になる。

Nagarei avatar Jan 06 '16 03:01 Nagarei

SFINAEではじく

どう書けばいいのか思いつかないんですが・・・

yumetodo avatar Jan 06 '16 03:01 yumetodo

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;

}

Nagarei avatar Jan 06 '16 05:01 Nagarei

    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

別ブランチに上の状態を上げました↓

yumetodo avatar Jan 06 '16 06:01 yumetodo

  1. test commitの状態だとpointu8i系の+=が実質使用不可になってしまう。
  2. ignore形式にしたらコンパイルが通った
  3. なぜtest commitのだとだめなのかという疑問は残るけれども、とりあえずignore形式の物を使うで良いのではないか

というわけでcommitしようと思ったのですが、作業データを消してしまったので、commitは後でにします。

Nagarei avatar Jan 06 '16 08:01 Nagarei

@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;
    }

だめでした。なんでや。

yumetodo avatar Jan 06 '16 09:01 yumetodo

試したらこれで行けました。

->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になってしまいます。

Nagarei avatar Jan 06 '16 10:01 Nagarei

そうなんですよねぇ・・・。 https://github.com/Nagarei/DxLibEx/commit/f3de959467fd39d75070e4ab620daff3657d0bd4 これの場合安定と信頼のIntelliSenseバグりが発生するし・・・。

整数型同士はsizeofみて、ほかは自力で判定書いて、これようのメタテンプレートつくります?

yumetodo avatar Jan 06 '16 10:01 yumetodo

メタテンプレートを新しく書いてもIntelliSenseバグりが治る確証がないんですよね...。 ただ、+=に限って言えば、赤線が出なければIntelliSenseがおかしくても気にならないとは思いますが。

Nagarei avatar Jan 06 '16 12:01 Nagarei

それっぽいもの作りましたが http://melpon.org/wandbox/permlink/ICHvO7j5mROuFOrJ 安定と信頼のIntelliSenseバグりからの、constexpr関数使ってるからVS2013で使えないのでダメですね。

もうSFINAEするのを諦めませんか?

yumetodo avatar Jan 07 '16 01:01 yumetodo

SFINAEがなくてもエラーになるのでSFINAEを外しても良いと思います。

Nagarei avatar Jan 07 '16 07:01 Nagarei

外してみました。

yumetodo avatar Jan 07 '16 09:01 yumetodo

うわぁ、江添さん、記事にしちゃった。びっくり。 yumetodoが本当に欲しかったもの | 本の虫 http://cpplover.blogspot.jp/2016/01/yumetodo.html

yumetodo avatar Jan 08 '16 02:01 yumetodo

という感じでNarrowing Conversionsを禁止するようにしてみたbranchを作りましたが、どうでしょう?

yumetodo avatar Jan 08 '16 03:01 yumetodo

そういえば

const dxle::size_c<std::uint8_t> s1 = { 3, 7 };
const auto s2 = s1 * 2;//dxle::size_c<int>

これは望ましい挙動なのだろうか。

yumetodo avatar Jan 08 '16 03:01 yumetodo

Narrowing Conversionsを禁止

IntelliSenseがちゃんと利いてますね。良かった。これで良いと思います。

挙動

std::uint8_t v1 =3;
const auto v2 = v1 * 2;//int

これに違和感を覚えるかどうかの話になってきますね...。

Nagarei avatar Jan 08 '16 03:01 Nagarei

IntelliSenseがちゃんと利いてますね。良かった。これで良いと思います。

というわけでpullrequest出しました

そういえば内部型のちがうクラス同士のキャストもNarrowing Conversionsではないならexplicit外すとかできるのだろうか、できるならした方がいいけれど。

yumetodo avatar Jan 08 '16 04:01 yumetodo

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に失敗せねばならんのか。

Nagarei avatar Jan 09 '16 13:01 Nagarei

ちなみに、VS2013 November CTPだとどうなりますか? (2015入れてから2013入れるのは難儀で、かといって2015アンインストールしてからというのも難儀なのです、すみません)

yumetodo avatar Jan 09 '16 15:01 yumetodo

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 スキップ ==========

Nagarei avatar Jan 10 '16 02:01 Nagarei

うわぁ・・・。 頑張って2013入れ直しますか・・・。

yumetodo avatar Jan 10 '16 03:01 yumetodo

value_typeが同じpoint_cへの暗黙の変換を許可したほうが良いと思いますが、どう思いますか?

Nagarei avatar Feb 26 '16 09:02 Nagarei

オーバーロード解決周りが不安ですね・・・。大丈夫なら許可して大丈夫だと思いますが

yumetodo avatar Feb 26 '16 09:02 yumetodo