GithubHelp home page GithubHelp logo

combinativeclass's Introduction

Combinative Class

The Combinative Class is a tiny tool that leverages C++23 features to provide an improved Curiously Recurring Template Pattern (CRTP) based implementation. This utility enables the combination of data and the implementation of methods based on these combinations, offering a clean and powerful approach to multiple inheritance without relying on virtual bases.

Quick Sample

#include "combinative.h"

using namespace combinative;

struct A{ int a; };
struct B{ int b; };
struct C{ int c; };
struct D{ int d; };

struct MethodA : impl_for<A>
{
    int FuncA(this auto&& self)
    {
        return self.a;
    }
};

struct MethodC : impl_for<C>
{
    int FuncC(this access_list self)
    {
        return self.cref().c;//tip friendly
    }
};

struct MethodAB : impl_for<A,B>
{
    int FuncAB(this access_list self)
    {
        auto [fa,fb] = self.cref();
        return fa.a + fb.b;
    }
};

using MyFuncSet = function_set<MethodA, MethodC, MethodAB>;

struct ObjectA : combine<MyFuncSet, A>
{
    ObjectA(int a_) { a = a_; }
};
struct ObjectAB : combine<ObjectA, B>
{
    ObjectAB(int a_,int b_) { a = a_; b= b_;}
};
struct ObjectAC : combine<ObjectA, C>
{
    ObjectAC(int a_,int c_) { a = a_; c= c_;}
};
struct ObjectABC : combine<ObjectAB, ObjectAC>
{
    ObjectABC(int a_,int b_,int c_) { a = a_; b= b_; c= c_; }
};

void quick_sample()
{
    ObjectA o1(1);
    o1.FuncA();
    ObjectAB o2(1,2);
    o2.FuncA();
    o2.FuncAB();
    ObjectAC o3(1,3);
    o3.FuncA();
    o3.FuncC();
    ObjectABC o4(1,2,3);
    o4.FuncA();
    o4.FuncC();
    o4.FuncAB();
};

Purpose

As the name suggest, it's for combining data and method to form different classes.

in the type_info sample , there is a type_info struct which contains the following fields

struct type_info
{
	size_t size;
	copy_constructor_ptr_t copy_constructor;
	move_constructor_ptr_t move_constructor;
	destructor_ptr_t destructor;
	uint64_t hash;
	const char* name;
};

but the type_info is not allow to move and copy.

thus a type_index is act as a warpper of type_info to referencing the type_info and provide some info access methods

class type_index
{
	const type_info* m_info;
public:
	type_index(const type_info& info) : m_info(&info) {}
	/* other methods to access info*/
};

this casue a problem that if one want to access any info of a type, the access action requires an indirection

you may wish that the hot properties can be inlinely cache in the type_index, in some cases which partial of a type's info is frequently visited,

class type_index_cache_hash
{
	const type_info* m_info;
	size_t m_hash;
};

class type_index_cache_destructor
{
	const type_info* m_info;
	size_t m_size;
	destructor_ptr_t m_destructor;
};
//...and more

or it may only need to store one or two properties of the type

class type_size_hash
{
	size_t m_size;
	size_t m_hash;
};
//...and more

for each version adding to the project

  • all method related to the changing properties need to be rewrite
  • convertion between each version need to be add
  • binary operator between each version need to be add

those issues are making it hard to maintain

the solution see sample_type_info.h. both CRTP implementation and Combinative Class implementation are provided

Feature

The Combinative Class basically an extension of CRTP with following feature

  • combination :
    • properties are divided into fragments
    • combinative class is a combination of fragments and it's matched method
    • combinative class can be the combination of combinative classes
  • access control :
    • fragment visibility defualt to be protected
    • combinative classes combination is default to be protected
    • method struct inheritance is public(may support function set access control in the future)
    • visibility can be explicitly control by warpping type in template pub prot and priv
    • friend declaration for CRTP parent is implied
  • method visibility friendly :
    • methods that don't match it's requirement won't appear in the final class
  • macro free :
    • zero exposed macro
  • intellisense friendly :
    • test on msvc tool chain : intellisense can infer the member and method of the combinative class

Feature Sample

#include "sample_type_info.h"
#include "combinative.h"

namespace sample
{

    using namespace combinative;

    struct FragmentA
    {
        int32_t a{};
    };
    struct FragmentB
    {
        int32_t b{};
    };
    struct FragmentC
    {
        int32_t c{};
    };
    struct FragmentD
    {
        int32_t c{};
    };

    struct Methods1 : impl_for<FragmentA, FragmentB>::exclude<FragmentC>
    {
        auto Auto_AB_noC(this auto &&self) { return self.a + self.b; }
    };

    struct Methods2 : impl_for<FragmentA, FragmentC>
    {
        auto Auto_AC(this auto &&self) { return self.a + self.c; }
    };

    struct Methods3 : impl_for<FragmentB, FragmentC>
    {
        auto Auto_BC(this auto &&self) { return self.b + self.c; }
    };

    struct SingleFragmentAccess : impl_for<FragmentC>::exclude<FragmentA, FragmentB>
    {
        auto TemplateCast_C_noAB(this auto &&self) { return self.as<FragmentC>().c; }
        auto EmbeddedCaster_C_noAB(this access_list self) { return self.cref().c; }
    };

    struct MultiFragmentAccess : impl_for<FragmentA, FragmentB, FragmentC>
    {
        auto Setter_ABC(this access_list self, int32_t a, int32_t b, int32_t c)
        {
            auto [fa, fb, fc] = self.ref();
            fa.a = a;
            fb.b = b;
            fc.c = c;
        }

        auto Auto_ABC(this auto &&self)
        {
            return self.a + self.b + self.c;
        }

        auto EmbeddedTupleCaster_ABC(this access_list self)
        {
            auto [fa, fb, fc] = self.cref();
            return fa.a + fb.b + fc.c;
        }

        auto FragmentCopy_ABC(this access_list self)
        {
            auto [fa, fb, fc] = self.val();
            return std::make_tuple(fa.a, fb.b, fc.c);
        }
    };

    struct PropertiesConflict : impl_for<FragmentC, FragmentD>
    {
        auto EmbeddedTupleCaster_CD(this access_list self)
        {
            auto [fc, fd] = self.cref();
            return fc.c + fd.c;
        }
        auto StaticCast_CD(this auto &&self)
        {
            auto &x = static_cast<FragmentC &>(self).c; // u may define a marco to simplify this cast
            auto &y = static_cast<FragmentD &>(self).c;
            return x + y;
        }
        auto Caster_CD(this auto &&self)
        {
            auto &x = caster<FragmentC>(self).cref().c;
            auto &y = caster<FragmentD>(self).cref().c;
            return x + y;
        }
        auto TemplateCast_CD(this auto &&self)
        {
            auto &x = self.as<FragmentC>().c; // this is unfriendly to properties tipping
            auto &y = self.as<FragmentD>().c;
            return x + y;
        }
    };

    using FuncSet1 = function_set<Methods1, Methods2, Methods3,
                                  SingleFragmentAccess,
                                  MultiFragmentAccess,
                                  PropertiesConflict>;

    struct CompareTag {};

    struct MethodsComp1 : impl_for<FragmentA>::exclude<FragmentB>::tag<CompareTag>
    {
        auto Comparable(this auto &&self) { return self.a; }
    };

    struct MethodsComp2 : impl_for<FragmentB>::exclude<FragmentA>::tag<CompareTag>
    {
        auto Comparable(this auto &&self) { return self.b; }
    };

    struct MethodsComp3 : impl_for<FragmentA, FragmentB>::tag<CompareTag>
    {
        auto Comparable(this auto &&self) { return self.a * self.b; }
    };

    using FuncSet2 = function_set<MethodsComp1, MethodsComp2, MethodsComp3>;

    auto operator<=>(
        std::derived_from<CompareTag> auto &a,
        std::derived_from<CompareTag> auto &b)
    {
        return a.Comparable() <=> b.Comparable();
    }

    using FuncSetFinal = function_set<FuncSet1, FuncSet2>;

    struct ObjectAB : combine<FuncSetFinal, pub<FragmentA>, FragmentB>
    {
    };
    static_assert(sizeof(ObjectAB) == 2 * sizeof(int32_t));

    struct ObjectAC : combine<FuncSetFinal, FragmentA, FragmentC>
    {
    };
    static_assert(sizeof(ObjectAC) == 2 * sizeof(int32_t));

    struct ObjectABC : combine<pub<ObjectAB>, ObjectAC>
    {
    };
    static_assert(sizeof(ObjectABC) == 3 * sizeof(int32_t));

    struct ObjectABC_ : combine<ObjectABC>::visibility_override<priv<FragmentA>>
    {
    };
    static_assert(sizeof(ObjectABC_) == sizeof(ObjectABC));

    struct ObjectCD : combine<ObjectABC, FragmentD>::remove<FragmentA, FragmentB>
    {
    };
    static_assert(sizeof(ObjectCD) == 2 * sizeof(int32_t));

    using is_a_accessible = decltype([](auto t) requires requires { t.a; } {});
    static_assert(std::invocable<is_a_accessible, ObjectABC>);
    static_assert(!std::invocable<is_a_accessible, ObjectABC_>);
}

using namespace sample;

int main()
{
    ObjectAB o1;
    o1.a = 1;
    o1.Auto_AB_noC();

    ObjectAC o2;
    o2.Auto_AC();

    ObjectABC o3;
    o3.Setter_ABC(1, 2, 3);
    o3.Auto_AC();
    o3.Auto_BC();
    o3.Auto_ABC();
    o3.EmbeddedTupleCaster_ABC(); // intellisense as some problem in tipping this
    o3.FragmentCopy_ABC();

    ObjectCD o4;
    o4.TemplateCast_C_noAB();
    o4.EmbeddedCaster_C_noAB();
    o4.EmbeddedTupleCaster_CD();
    o4.StaticCast_CD();
    o4.Caster_CD();
    o4.TemplateCast_CD();

    o1 <=> o2;
    o2 <=> o3;
    o1 <=> o3;

#ifdef TYPE_INFO_SAMPLE
    generic::sample_use();
#endif // TYPE_INFO_SAMPLE
}

a more realistic sample is provide in sample_type_info.h

combinativeclass's People

Contributors

stellarwarp avatar

Stargazers

Li Jin avatar Max Qian avatar

Watchers

 avatar

Forkers

linecode

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.