Salam untuk semua pembaca.
Apa artikel (atau tugas artikel) : jawaban praktis untuk pertanyaan "apakah mungkin untuk membuat proyek besar sedemikian rupa sehingga benar-benar meninggalkannya dynamic_cast
pada tahap pelaksanaan?", Di mana dengan proyek besar itu berarti proyek di mana tidak ada lagi orang yang akan menyimpan seluruh kode seluruh basis proyek.
Jawaban awal : YA, itu mungkin - dimungkinkan untuk membuat mekanisme yang memungkinkan Anda untuk menyelesaikan masalah dynamic_cast pada tahap kompilasi , tetapi - ini tidak mungkin diterapkan dalam praktik karena alasan seperti: (1) dari awal, proyek target harus dibangun sesuai dengan aturan yang telah ditentukan, sebagai hasilnya apa yang harus diambil dan menerapkan teknik ke proyek yang ada, itu sangat memakan waktu (2) peningkatan yang signifikan dalam kompleksitas kode dari sudut pandang keterbacaannya di tempat-tempat tertentu, di mana, pada kenyataannya, logika digantidynamic_cast
penggunaan templat yang diusulkan di bawah ini (3), yang mungkin tidak dapat diterima dalam beberapa proyek karena alasan ideologis dari mereka yang bertanggung jawab untuk itu (4) minat penulis semata-mata dalam memberikan jawaban atas pertanyaan yang diajukan, dan bukan dalam menciptakan mekanisme solusi universal dan nyaman tugas (bagaimanapun juga, dalam praktiknya tidak perlu menyelesaikan masalah yang tidak mendesak).
Ide implementasi
Itu didasarkan pada gagasan daftar jenis, dijelaskan oleh Andrei Alexandrescu dan diimplementasikan olehnya di perpustakaan Loki . Ide ini telah disempurnakan pada poin-poin berikut (poin yang ditandai *
berarti bahwa pada poin ini penulis artikel tidak setuju dengan visi daftar tipe Alexandrescu):
- menambahkan kemampuan untuk menghasilkan daftar panjang sembarang jenis tanpa menggunakan makro dan / atau struktur templat, dengan jumlah parameter templat yang sama dengan panjang daftar yang dibuat;
- menambahkan kemampuan untuk menghasilkan daftar jenis berdasarkan jenis dan / atau daftar jenis yang ada dalam kombinasi sembarangnya;
- * menghilangkan kemampuan untuk membuat daftar jenis yang elemennya dapat berupa daftar jenis;
- *
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
β .
, .
Terima kasih kepada semua orang yang membaca artikel :) - Saya akan senang mengetahui pengalaman, pendapat Anda, atau, mungkin, bahkan solusi untuk masalah yang dijelaskan dalam artikel di komentar.