samedi 28 mars 2020

(C++11) Is there any way to limit scope of template parameters on a specific class?

■ Problem Definition_________________________

!!You can skip here and directly go to■ Question Summary !!

I designed a flexible but memory-efficient class that suits various situations, having only selective traits within it : link

Plus, I gave ID for each trait, which is used when a user wants to request only specific traits of the class.

I wrote my own class satisfying these properties, using multiple inheritances of unnamed enums with variadic template.

See below :

▼ TriTraits.h

struct TriT
{
    struct Centroid
    {
        Point3D centroid;
        struct ID { enum : utin8_t { CENTROID = 1 }; }; // 0000 0001
    };

    struct Area
    {
        double area;
        struct ID { enum : utin8_t { AREA = 2 }; }; // 0000 0010
    };

    struct Perimeter
    {
        double perimeter;
        struct ID { enum : utin8_t { PERIMETER = 4 }; }; // 0000 0100
    };
    ... // More traits...
};

▼ Triangle.h

#include "TriTraits.h"

enum class TRI_TRAIT_ID : uint8_t {}; // strong type
template<class... Traits>
struct TriangleT : Traits...
{
    struct IDs : Traits::ID...
    {
        enum : uint8_t {
            NONE = 0, // 0000 0000
            ALL = 255 // 1111 1111
        };
    };
    void ComputeTrait(TRI_TRAIT_ID _requestedIDs)
    {
        ... // Implementation will be written somehow, using bitwise & operator.
    }
};

For example, if one defines one's own triangle type, MyTri<TriT::Area, TriT::Perimeter> myTri, what compiler sees may look like this :

struct MyTri
{
    double area;
    double perimeter;

    struct IDs // Since enum can't be inherited, it has 3 individual unnamed enums in it
    {
        enum : uint8_t { AREA = 2 };
        enum : uint8_t { PERIMETER = 4 };
        enum : uint8_t {
            NONE = 0,
            ALL = 255
        };
    };
} myTri;

This custom triangle type will compute some of its traits by calling ComputeTraits(...) with bitwise |operator, like this :

myTri.ComputeTraits(MyTri::IDs::AREA | MyTri::IDs::PERIMETER); // Compute area and perimeter of it

The problem rises here : suppose there are Rectangle.h and Pentagon.h, ... NthPolygon.h, in the same manner..

I want each of my custom polygon types to get a strongly-typed parameter for its ComputeTrait(...)(So I used enum class for it), to prevent silly operations taking mixed types of polygon traits, such as

myTri.ComputeTraits(MyTri::IDs::AREA | MyRect::IDs::PERIMETER); // Mixed traits of a triangle and of  a rectangle.

So I want to overload |operator which takes only unnamed enums in each scope of Polygon::IDs::.

I tried writing the overloaded template operator inside the private scope of NthPolygon::IDs::, but this failed since in-class operator overloading always takes the first parameter as itself : link

Making it friend of global scope also failed, since there will be more than 1 overloaded |operators for every N-th Polygon classes.

enum class N_TH_TRAIT_ID : uint8_t {}; // strong type
struct NthPolygon
{
    ...
    struct IDs
    {
        ...
     private:
        template<typename TRAIT_ID1, typename TRAIT_ID2>
        N_TH_TRAIT_ID operator|(TRAIT_ID1 _id1, TRAIT_ID2 _id2) // Error : too many operators for this operator function
        { return static_cast<N_TH_TRAIT_ID>(static_cast<utin8_t>(_id1) | static_cast<utin8_t>(_id2)); }

        template<typename TRAIT_ID1, typename TRAIT_ID2>
        friend N_TH_TRAIT_ID operator|(TRAIT_ID1 _id1, TRAIT_ID2 _id2) // Error : more than 1 operator "|" matches these operands
        { return static_cast<N_TH_TRAIT_ID>(static_cast<utin8_t>(_id1) | static_cast<utin8_t>(_id2)); }
    };
} myTri;

■ Question Summary_________________________

In the situation below, how can one make the template overloaded operator| take only parameters(unnamed enums) from the specific class?

enum class STRONG_TYPE_TRI_TRAIT_ID : uint8_t {};
struct Triangle
{
    struct IDs {
        enum : utin8_t { A = 1 };
        enum : utin8_t { B = 2 };
        enum : utin8_t { C = 3 };
    };
};
template<typename TRI_TRAIT_ID1, typename TRI_TRAIT_ID2>
STRONG_TYPE_TRI_TRAIT_ID operator|(TRI_TRAIT_ID1 _id1, TRI_TRAIT_ID2 _id2)
{ return static_cast<STRONG_TYPE_TRI_TRAIT_ID>(static_cast<uint8_t>(_id1) | static_cast<uint8_t>(_id2)); }
enum class STRONG_TYPE_RECT_TRAIT_ID : uint8_t {};
struct Rectangle
{
    struct IDs {
        enum : utin8_t { A = 1 };
        enum : utin8_t { B = 2 };
        enum : utin8_t { C = 3 };
    };
};
template<typename RECT_TRAIT_ID1, typename RECT_TRAIT_ID2>
STRONG_TYPE_RECT_TRAIT_ID operator|(RECT_TRAIT_ID1 _id1, RECT_TRAIT_ID2 _id2)
{ return static_cast<STRONG_TYPE_RECT_TRAIT_ID >(static_cast<uint8_t>(_id1) | static_cast<uint8_t>(_id2)); }
int main(void)
{
    Triangle::IDs::A | Triangle::IDs::B;  // OK
    Triangle::IDs::A | Rectangle::IDs::B; // Error
    ...
    return 0;
}

Aucun commentaire:

Enregistrer un commentaire