mardi 17 mars 2020

How to design a class with switchable member variables at run-time in C++?


| ■ Probelm definition_____________________________________________

I'm trying to design a super-flexible, but memory efficient module, having several members that are ON/OFF-switchable respectably depending on the situation.

Which variables the module will own should be determined at the run-time.

I somehow made similar one working at the compile-time, using macros and enumerator flags :

▼ TraitSwitch.h

#pragma once
// Macros to switch-off source codes themselves.
#define ON  1
#define OFF 0

#define TRI_AREA_INFO      ON
#define TRI_CENTROID_INFO  ON
#define TRI_NORMAL_INFO    OFF
...

▼ TriangleTraits.h

#pragma once
#include <cstdint>
#include "TraitSwitch.h"

enum TriangleTrait : uint8_t
{
    NONE          = 0,   // 0000 0000

#if (TRI_AREA_INFO == ON)
    AREA          = 1,   // 0000 0001
#endif

#if (TRI_CENTROID_INFO == ON)
    CENTROID      = 2,   // 0000 0010
#endif

#if (TRI_NORMAL_INFO == ON)
    NORMAL_VECTOR = 4,   // 0000 0100
#endif
    ... // more traits

    ALL           = 255  // 1111 1111
}
// Need some additional overloaded bitwise-operators (&, |, |=, etc ...)

▼ Triangle.h

#pragma once
#include "TriangleTraits.h"

class Triangle
{
public:
    Triangle() {}
    ~Triangle() {}

#if (TRI_AREA_INFO == ON)
    double area;
#endif

#if (TRI_CENTROID_INFO == ON)
    double centroid[3]; // x, y, z
#endif

#if (TRI_NORMAL_INFO == ON)
    double normal[3]; // x, y, z
#endif
    ...

    TriangleTrait alreadyComputed; // To avoid redundant works.
    void ComputeTraits(TriangleTrait _trait)
    {
        if (((_trait & TriangleTrait::AREA) != 0) 
            && ((_trait & alreadyComputed) == 0))
        {
            this->ComputeArea();
            alreadyComputed |= TriangleTrait::AREA;
        }
        ... // do the same things for centroid, normal
    }

private:
    void ComputeArea();
    void ComputeCentroid();
    void ComputeNormal();
    ...
}

then, Ctrl + SpaceBar(C++ IntelliSense) on the object may show : this

▼ main.cpp

#include <iostream>
#include "Triangle.h"

int main(void)
{
    Triangle tri;
    tri.ComputeTraits(TriangleTrait::AREA | TriangleTrait::CENTROID);

    std::cout << "area : " << tri.area << "m²" << std::endl;
    std::cout << "centroid : (" 
        << tri.centroid[0] << "," 
        << tri.centroid[1] << "," 
        << tri.centroid[2] << ")" << std::endl;
}

Firstly Triangle.h looks quite ugly, and even if it looks good, this method determines class members at the compile-time, anyway.


| ■ Question summary_____________________________________________

"How to design a (template-)class with switchable members, which are determined at the run-time."

Here's the very thing what I want :

▼ main.cpp

...
int main(void)
{
    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA)> tri1; // This owns area info only
    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA | CENTROID)> tri2; // This owns area & centroid info
    Triangle<__MACRO_DEFINED_TRAIT_SWITCH(AREA | CENTROID | NORMAL)> tri3; // This owns area & centroid & normal vector info
}

I guess using templates combined with macros (with Tag-dispatching method, maybe?) will do exactly what I want, but have no any clear idea.

Aucun commentaire:

Enregistrer un commentaire