Grüße an alle Leser.
Was ist der Artikel (oder die Aufgabe des Artikels) : eine praktische Antwort auf die Frage " dynamic_cast
Ist es möglich, ein großes Projekt so zu erstellen, dass es in der Ausführungsphase vollständig aufgegeben wird ?", Wobei es sich bei einem großen Projekt um ein Projekt handelt, bei dem es keine Person mehr gibt, die den gesamten Code behalten würde die gesamte Projektbasis.
Vorläufige Antwort : JA, es ist möglich - es ist möglich, einen Mechanismus zu erstellen, mit dem Sie das Problem dynamic_cast in der Kompilierungsphase lösen können , aber - dies wird in der Praxis aus folgenden Gründen wahrscheinlich nicht angewendet: (1) Das Zielprojekt sollte daher von Anfang an nach vorgegebenen Regeln erstellt werden Was zu tun und die Technik auf ein bestehendes Projekt anzuwenden ist, ist sehr zeitaufwändig (2) eine signifikante Erhöhung der Komplexität des Codes im Hinblick auf seine Lesbarkeit an bestimmten Stellen, an denen tatsächlich die Logik ersetzt wirddynamic_cast
Für die Verwendung der nachstehend vorgeschlagenen Vorlagen (3), die in einigen Projekten aus ideologischen Gründen der Verantwortlichen inakzeptabel sein können (4), besteht das Interesse des Autors ausschließlich darin, eine Antwort auf die gestellte Frage zu geben und keinen universellen und bequemen Lösungsmechanismus zu schaffen die Aufgabe (schließlich ist es in der Praxis nicht notwendig, nicht dringende Probleme zu lösen).
Implementierungsidee
Es basierte auf der Idee einer Liste von Typen, die von Andrei Alexandrescu beschrieben und von ihm in der Loki-Bibliothek implementiert wurde . Diese Idee wurde in folgenden Punkten verfeinert (markierte Punkte *
bedeuten, dass der Autor des Artikels in diesem Punkt nicht mit Alexandrescus Vision von Typlisten übereinstimmt):
- Es wurde die Möglichkeit hinzugefügt, eine Liste mit Typen beliebiger Länge ohne Verwendung von Makros und / oder Vorlagenstrukturen zu erstellen, wobei die Anzahl der Vorlagenparameter der Länge der erstellten Liste entspricht.
- fügte die Möglichkeit hinzu, eine Liste von Typen basierend auf den Typen und / oder einer vorhandenen Liste von Typen in ihrer willkürlichen Kombination zu generieren;
- * Die Möglichkeit, Typenlisten zu erstellen, deren Elemente Listen von Typen sein können, wurde entfernt.
- *
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
— .
, .
Vielen Dank an alle, die den Artikel gelesen haben :) - Ich freue mich über Ihre Erfahrungen, Ihre Meinung oder vielleicht sogar über die Lösung des im Artikel in den Kommentaren beschriebenen Problems.