dynamic_cast au moment de la compilation

Salutations à tous les lecteurs.







Qu'est-ce que l'article (ou la tâche de l'article) : une réponse pratique à la question "est-il possible de créer un grand projet de manière à l'abandonner complètement dynamic_cast



au stade de l'exécution?", Où par un grand projet cela signifie celui dans lequel il n'y a plus personne qui garderait le code entier toute la base du projet.







Réponse préliminaire : OUI, c'est possible - il est possible de créer un mécanisme qui vous permet de résoudre le problème dynamic_cast au stade de la compilation , mais - il est peu probable que cela soit appliqué en pratique pour des raisons telles que: (1) dès le début, le projet cible doit être construit selon des règles prédéterminées, en conséquence que prendre et appliquer la technique à un projet existant, cela prend beaucoup de temps (2) une augmentation significative de la complexité du code du point de vue de la facilité de lisibilité à certains endroits où, en fait, la logique est remplacéedynamic_cast



à l'utilisation des modèles proposés ci-dessous (3), qui peuvent être inacceptables dans certains projets pour des raisons idéologiques de ceux qui en sont responsables (4) l'intérêt de l'auteur est uniquement de donner une réponse à la question posée, et non de créer un mécanisme universel et pratique de résolution la tâche (après tout, il n'est pas nécessaire en pratique de résoudre des problèmes qui ne sont pas urgents).







Idée de mise en œuvre



Il était basé sur l'idée d'une liste de types, décrite par Andrei Alexandrescu et implémentée par lui dans la bibliothèque Loki . Cette idée a été affinée sur les points suivants (des points marqués *



signifient que sur ce point l'auteur de l'article n'est pas d'accord avec la vision d'Alexandrescu des listes de types):







  • ajouté la possibilité de générer une liste de longueur arbitraire de types sans utiliser de macros et / ou de structures de modèle, avec le nombre de paramètres de modèle égal à la longueur de la liste créée;
  • ajouté la possibilité de générer une liste de types basée sur le (s) type (s) et / ou une ou plusieurs liste (s) de types existante (s) dans leur combinaison arbitraire;
  • * supprimé la possibilité de créer des listes de types dont les éléments peuvent être des listes de types;
  • * 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();
}
      
      





  1. 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



      .


  2. 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



    — , .







  3. 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



    , .







  4. 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



    .









  5. , 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



      e value



      .


    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



    — .

    , .







    Merci à tous ceux qui ont lu l'article :) - Je serai heureux de connaître votre expérience, votre opinion sur, ou peut-être même la solution au problème décrit dans l'article dans les commentaires.








All Articles