すべての読者へのご挨拶。
記事(または記事のタスク)とは:「dynamic_cast
実行段階で完全に放棄するような方法で大規模なプロジェクトを作成することは可能ですか?」という質問に対する実際的な答えです。大規模なプロジェクトとは、コード全体を保持する人がもはやいないことを意味します。プロジェクトベース全体。
予備的な答え:はい、それは可能である-あなたが解決することを可能にするメカニズムを作成することが可能であるのdynamic_castの問題をコンパイル段階では、しかし-これは、などの理由で、実際に適用されることはほとんどありません:(1)非常に始まり、対象のプロジェクトは、結果として、所定の規則に従って構築されなければならないから何を採用して既存のプロジェクトにこの手法を適用するかは、非常に時間がかかります(2)実際にはロジックが置き換えられている特定の場所での読みやすさの観点から、コードの複雑さが大幅に増加します。dynamic_cast
以下に提案するテンプレートの使用(3)は、責任者のイデオロギー上の理由から一部のプロジェクトでは受け入れられない場合があります(4)作成者の関心は、提起された質問への回答のみであり、普遍的で便利なソリューションメカニズムの作成ではありません。タスク(結局のところ、緊急ではない問題を解決することは実際には必要ありません)。
実装のアイデア
これは、Andrei Alexandrescuによって記述され、Lokiライブラリに実装されたタイプのリストのアイデアに基づいていました。このアイデアは、次の点で洗練されました(マークされた点*
は、この点で、記事の作成者がアレクサンドレスクのタイプリストのビジョンに同意しないことを意味します)。
- マクロやテンプレート構造を使用せずに、作成されたリストの長さに等しいテンプレートパラメータの数で、タイプの任意の長さのリストを生成する機能が追加されました。
- タイプおよび/または任意の組み合わせのタイプの既存のリストに基づいてタイプのリストを生成する機能が追加されました。
- *要素がタイプのリストになることができるタイプのリストを作成する機能を削除しました。
- *
MostDerived
DerivedToFront
, .. (1) , , , , , (2) , , - , , ; -
static_assert
, , ; -
RemoveFromSize
,CutFromSize
.
, , (https://github.com/AlexeyPerestoronin/Cpp_TypesList), , , .
, , , .
#include <gtest/gtest.h>
#include <TypesList.hpp>
#include <memory>
class A {
public:
using BASE_t = TL::Refine_R<TL::CreateTypesList_R<void>>;
A() {}
A(int a) {
buffer << ' ' << a;
}
virtual void F1() = 0;
protected:
std::stringstream buffer;
};
class B : public A {
public:
using BASE_t = TL::Refine_R<TL::CreateTypesList_R<A, A::BASE_t>>;
B() {}
B(int a, int b)
: A(a) {
buffer << ' ' << b;
}
virtual void F1() override {
std::cout << "class::B" << buffer.str() << std::endl;
}
};
class C : public B {
public:
using BASE_t = TL::Refine_R<TL::CreateTypesList_R<B, B::BASE_t>>;
C() {}
C(int a, int b, int c)
: B(a, b) {
buffer << ' ' << c;
}
virtual void F1() override {
std::cout << "class::C" << buffer.str() << std::endl;
}
};
class D : public C {
public:
using BASE_t = TL::Refine_R<TL::CreateTypesList_R<C, C::BASE_t>>;
D() {}
D(int a, int b, int c, int d)
: C(a, b, c) {
buffer << ' ' << d;
}
virtual void F1() override {
std::cout << "class::D" << buffer.str() << std::endl;
}
};
TEST(Check_class_bases, test) {
{
using TClass = A;
EXPECT_EQ(TClass::BASE_t::size, 1);
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>));
}
{
using TClass = B;
EXPECT_EQ(TClass::BASE_t::size, 2);
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>));
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, A>));
}
{
using TClass = C;
EXPECT_EQ(TClass::BASE_t::size, 3);
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>));
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, A>));
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, B>));
}
{
using TClass = D;
EXPECT_EQ(TClass::BASE_t::size, 4);
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>));
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, A>));
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, B>));
EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, C>));
}
}
// TT - Type to Type
template<class Type, class BASE_t>
struct T2T {
std::shared_ptr<Type> value;
using PossibleTo_t = BASE_t;
};
template<class To, class From, class... Arguments>
auto T2TMake(Arguments&&... arguments) {
T2T<To, TL::Refine_R<TL::CreateTypesList_R<From, From::BASE_t>>> result{};
result.value = std::make_shared<From>(arguments...);
return result;
}
template<class BASE_t>
void AttemptUse(T2T<A, BASE_t> tb) {
static_assert(TL::IsInList_R<BASE_t, C>, "this function can to use only with C-derivative params");
tb.value->F1();
}
TEST(T2TMake, test) {
{
auto value = T2TMake<A, B>();
using TClass = decltype(value)::PossibleTo_t;
EXPECT_EQ(TClass::size, 3);
EXPECT_TRUE((TL::IsInList_R<TClass, void>));
EXPECT_TRUE((TL::IsInList_R<TClass, A>));
EXPECT_TRUE((TL::IsInList_R<TClass, B>));
// AttemptUse(value); // compilation error
}
{
auto value = T2TMake<A, B>(1, 2);
using TClass = decltype(value)::PossibleTo_t;
EXPECT_EQ(TClass::size, 3);
EXPECT_TRUE((TL::IsInList_R<TClass, void>));
EXPECT_TRUE((TL::IsInList_R<TClass, A>));
EXPECT_TRUE((TL::IsInList_R<TClass, B>));
// AttemptUse(value); // compilation error
}
{
auto value = T2TMake<A, C>();
using TClass = decltype(value)::PossibleTo_t;
EXPECT_EQ(TClass::size, 4);
EXPECT_TRUE((TL::IsInList_R<TClass, void>));
EXPECT_TRUE((TL::IsInList_R<TClass, A>));
EXPECT_TRUE((TL::IsInList_R<TClass, B>));
EXPECT_TRUE((TL::IsInList_R<TClass, C>));
AttemptUse(value);
}
{
auto value = T2TMake<A, C>(1, 2, 3);
using TClass = decltype(value)::PossibleTo_t;
EXPECT_EQ(TClass::size, 4);
EXPECT_TRUE((TL::IsInList_R<TClass, void>));
EXPECT_TRUE((TL::IsInList_R<TClass, A>));
EXPECT_TRUE((TL::IsInList_R<TClass, B>));
EXPECT_TRUE((TL::IsInList_R<TClass, C>));
AttemptUse(value);
}
{
auto value = T2TMake<A, D>();
using TClass = decltype(value)::PossibleTo_t;
EXPECT_EQ(TClass::size, 5);
EXPECT_TRUE((TL::IsInList_R<TClass, void>));
EXPECT_TRUE((TL::IsInList_R<TClass, A>));
EXPECT_TRUE((TL::IsInList_R<TClass, B>));
EXPECT_TRUE((TL::IsInList_R<TClass, C>));
EXPECT_TRUE((TL::IsInList_R<TClass, D>));
AttemptUse(value);
}
{
auto value = T2TMake<A, D>(1, 2, 3, 4);
using TClass = decltype(value)::PossibleTo_t;
EXPECT_EQ(TClass::size, 5);
EXPECT_TRUE((TL::IsInList_R<TClass, void>));
EXPECT_TRUE((TL::IsInList_R<TClass, A>));
EXPECT_TRUE((TL::IsInList_R<TClass, B>));
EXPECT_TRUE((TL::IsInList_R<TClass, C>));
EXPECT_TRUE((TL::IsInList_R<TClass, D>));
AttemptUse(value);
}
}
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
class A
class A { public: using BASE_t = TL::Refine_R<TL::CreateTypesList_R<void>>; A() {} A(int a) { buffer << ' ' << a; } virtual void F1() = 0; protected: std::stringstream buffer; };
class A
— - , :using BASE_t = TL::Refine_R<TL::CreateTypesList_R<void>>;
, .
:
TL::CreateTypesList_R
— , .TL::Refine_R
— , , .
.. ,void
.
class B
class B : public A { public: using BASE_t = TL::Refine_R<TL::CreateTypesList_R<A, A::BASE_t>>; B() {} B(int a, int b) : A(a) { buffer << ' ' << b; } virtual void F1() override { std::cout << "class::B" << buffer.str() << std::endl; } };
, ,
BASE_t
— , .
class C
class C : public B { public: using BASE_t = TL::Refine_R<TL::CreateTypesList_R<B, B::BASE_t>>; C() {} C(int a, int b, int c) : B(a, b) { buffer << ' ' << c; } virtual void F1() override { std::cout << "class::C" << buffer.str() << std::endl; } };
, ,
BASE_t
, .
class D
class D : public C { public: using BASE_t = TL::Refine_R<TL::CreateTypesList_R<C, C::BASE_t>>; D() {} D(int a, int b, int c, int d) : C(a, b, c) { buffer << ' ' << d; } virtual void F1() override { std::cout << "class::D" << buffer.str() << std::endl; } };
, D
BASE_t
.
-
,TL::IsInList_R<TypesList, Type>
true
,Type
TypesList
,false
— .
TEST(Check_class_bases, test) { { using TClass = A; EXPECT_EQ(TClass::BASE_t::size, 1); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>)); } { using TClass = B; EXPECT_EQ(TClass::BASE_t::size, 2); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>)); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, A>)); } { using TClass = C; EXPECT_EQ(TClass::BASE_t::size, 3); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>)); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, A>)); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, B>)); } { using TClass = D; EXPECT_EQ(TClass::BASE_t::size, 4); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, void>)); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, A>)); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, B>)); EXPECT_TRUE((TL::IsInList_R<TClass::BASE_t, C>)); } }
, :
class A
,class B
,class C
class D
, —BASE_t
.
// T2T - Type to Type template<class Type, class BASE_t> struct T2T { std::shared_ptr<Type> value; using PossibleTo_t = BASE_t; };
value
Type
PossibleTo_t
value
, ( )Type
.
T2T
template<class To, class From, class... Arguments> auto T2TMake(Arguments&&... arguments) { T2T<To, TL::Refine_R<TL::CreateTypesList_R<From, From::BASE_t>>> result{}; result.value = std::make_shared<From>(arguments...); return result; }
T2TMake
:
From
— ,T2T
;To
—T2T
;Arguments
— .
, ,From
To
,TL::Refine_R<TL::CreateTypesList_R<From, From::BASE_t>>
T2T
evalue
.
T2T
template<class BASE_t> void AttemptUse(T2T<A, BASE_t> tb) { static_assert(TL::IsInList_R<BASE_t, C>, "this function can to use only with C-derivative params"); tb.value->F1(); }
, , , , — , — .
TEST(T2TMake, test) { { auto value = T2TMake<A, B>(); using TClass = decltype(value)::PossibleTo_t; EXPECT_EQ(TClass::size, 3); EXPECT_TRUE((TL::IsInList_R<TClass, void>)); EXPECT_TRUE((TL::IsInList_R<TClass, A>)); EXPECT_TRUE((TL::IsInList_R<TClass, B>)); // AttemptUse(value); // compilation error } { auto value = T2TMake<A, B>(1, 2); using TClass = decltype(value)::PossibleTo_t; EXPECT_EQ(TClass::size, 3); EXPECT_TRUE((TL::IsInList_R<TClass, void>)); EXPECT_TRUE((TL::IsInList_R<TClass, A>)); EXPECT_TRUE((TL::IsInList_R<TClass, B>)); // AttemptUse(value); // compilation error } { auto value = T2TMake<A, C>(); using TClass = decltype(value)::PossibleTo_t; EXPECT_EQ(TClass::size, 4); EXPECT_TRUE((TL::IsInList_R<TClass, void>)); EXPECT_TRUE((TL::IsInList_R<TClass, A>)); EXPECT_TRUE((TL::IsInList_R<TClass, B>)); EXPECT_TRUE((TL::IsInList_R<TClass, C>)); AttemptUse(value); } { auto value = T2TMake<A, C>(1, 2, 3); using TClass = decltype(value)::PossibleTo_t; EXPECT_EQ(TClass::size, 4); EXPECT_TRUE((TL::IsInList_R<TClass, void>)); EXPECT_TRUE((TL::IsInList_R<TClass, A>)); EXPECT_TRUE((TL::IsInList_R<TClass, B>)); EXPECT_TRUE((TL::IsInList_R<TClass, C>)); AttemptUse(value); } { auto value = T2TMake<A, D>(); using TClass = decltype(value)::PossibleTo_t; EXPECT_EQ(TClass::size, 5); EXPECT_TRUE((TL::IsInList_R<TClass, void>)); EXPECT_TRUE((TL::IsInList_R<TClass, A>)); EXPECT_TRUE((TL::IsInList_R<TClass, B>)); EXPECT_TRUE((TL::IsInList_R<TClass, C>)); EXPECT_TRUE((TL::IsInList_R<TClass, D>)); AttemptUse(value); } { auto value = T2TMake<A, D>(1, 2, 3, 4); using TClass = decltype(value)::PossibleTo_t; EXPECT_EQ(TClass::size, 5); EXPECT_TRUE((TL::IsInList_R<TClass, void>)); EXPECT_TRUE((TL::IsInList_R<TClass, A>)); EXPECT_TRUE((TL::IsInList_R<TClass, B>)); EXPECT_TRUE((TL::IsInList_R<TClass, C>)); EXPECT_TRUE((TL::IsInList_R<TClass, D>)); AttemptUse(value); } }
dynamic_cast
— .
, .
記事を読んだすべての人に感謝します:)-コメントの記事に記載されている問題の経験、意見、またはおそらく解決策さえ知ってうれしいです。