■ 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 |operator
s 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;
}