Is it possible to change the behavior of default value initialization of a scoped enum in C++ 20 ?
For exemple, in the following code, I'd like that a MyEnum variable to be initialized automaticatly with MyEnum::No when declared with MyEnum myValue{};
using MyEnumRootType = unsigned int;
using MyEnum = enum class EMyEnum: MyEnumRootType {
Yes = 0x1,
No = 0x2
};
int main() {
const MyEnum myValue{}; // <- 0
std::cout << static_cast<MyEnumRootType>(myValue);
}
edit:
I'v tried to write a wrapper like Benjamin Buch suggested (thanks).
I'm using template because I want it to be reusable.
I managed to handle a default value but I'm struggling to make it act like a real scoped enum (last line of main doesn't compile).
#include <concepts>
using MyEnumRootType = unsigned int;
enum class EMyEnum: MyEnumRootType {
Yes = 0x1,
No = 0x2
};
template<typename T>
concept ScopedEnum = std::is_enum_v<T> && !std::is_convertible_v<T, int>;
template<typename Enum, auto DefaultValue>
requires ScopedEnum<Enum>
class EnumWrapper{
public:
EnumWrapper(): value_(DefaultValue) {}
EnumWrapper& operator=(Enum e) { value_ = e; return *this; }
operator Enum() const { return value_; }
private:
Enum value_;
};
using MyEnum = EnumWrapper<EMyEnum, EMyEnum::No>;
int main() {
MyEnum a{};
MyEnum b = MyEnum::Yes; // how can I make this works ?
}
Can I do something like this in a template https://en.cppreference.com/w/cpp/language/enum#Using-enum-declaration ?
That's not possible directly for an enumeration (except if you are willing to change the value of No to 0x0). Enumerations are not class types (even if there is class in enum class), so you can't affect their behavior like you can with member functions/constructors/destructors/etc of classes.
Instead you'll have to make a class wrapper around it in which you can define a default constructor to do whatever you want.
It is only possible to write a wrapper class to achieve this behavior. Use using enum to address the values of your enumeration directly via the wapper class. Define a data member of the enumeration type and a cast operator for this type.
using MyEnumRootType = unsigned int;
enum class EMyEnum: MyEnumRootType {
Yes = 0x1,
No = 0x2
};
struct MyEnum {
using enum EMyEnum;
constexpr operator EMyEnum() const noexcept {
return value;
}
EMyEnum value = No;
};
int main() {
constexpr MyEnum myValue{};
static_assert(myValue != MyEnum::Yes);
static_assert(myValue == MyEnum::No);
static_assert(myValue != EMyEnum{});
}
Related
I have a third-party unscoped enum (that I can't modify) that I'd really like to cast to my own scoped enum. How can I provide something like a conversion operator?
What I'd like to do is something like this:
#include <iostream>
enum ThirdPartyLetter {
A=4,
B=5
};
enum class MyNumber {
ONE=1,
TWO=2
// This doesn't compile, of course
/*Number(const ThirdPartyLetter& rhs) {
if(rhs == ThirdPartyLetter::A) {
return ONE;
}
else {
return TWO;
}
}*/
};
int main() {
ThirdPartyLetter letter = ThirdPartyLetter::A;
MyNumber number = static_cast<MyNumber>(letter);
// Without the cast, this prints 4 (an invalid enum value!)
std::cout << int(number) << std::endl;
}
Is there a way to provide some kind of casting from ThirdPartyNumber to MyNumber?
An idiomatic way to do that at compile-time in C++ is using traits.
As an example:
enum Foo { ONE, TWO };
enum Bar { THREE, FOUR };
template<Foo> struct conv;
template<> struct conv<Foo::ONE> { static constexpr Bar value = Bar::THREE; };
template<> struct conv<Foo::TWO> { static constexpr Bar value = Bar::FOUR; };
If you want to do that at runtime, maybe a switch is well suited.
Anyway, you can still use traits to centralize your conversion logic and do something like this:
Bar get(Foo choice) {
switch(choice) {
case Foo::ONE:
return conv<ONE>::value;
case Foo::TWO:
return conv<TWO>::value;
}
}
Why does the compiler complain here?
enum jit_ptx_type {f32=0,f64=1,u16=2,u32=3,u64=4,s16=5,s32=6,s64=7,u8=8,b16=9,b32=10,b64=11,pred=12 };
//
// MATCHING C TYPES TO PTX TYPES
//
template<class T> struct jit_type {};
template<> struct jit_type<float> { enum { value = jit_ptx_type::f32 }; };
template<> struct jit_type<double> { enum { value = jit_ptx_type::f64 }; };
template<> struct jit_type<int> { enum { value = jit_ptx_type::s32 }; };
template<> struct jit_type<bool> { enum { value = jit_ptx_type::pred }; };
later in the code:
some_func( float val ) {
jit_ptx_type type = jit_type<float>::value; // compiler complains here
}
Compiler message:
error: cannot convert ‘jit_type<float>::<anonymous enum>’ to ‘jit_ptx_type’ in assignment
It's strange! If I put these lines into a separate small example file it works.
I'd go for making the outer enum into a scoped enum:
enum class jit_ptx_type {
f32=0, //note the =x is unnecessary here
f64=1,
u16=2,
u32=3,
u64=4,
s16=5,
s32=6,
s64=7,
u8=8,
b16=9,
b32=10,
b64=11,
pred=12
};
Now you don't pollute the surrounding scope with all of those identifiers and you need scope qualifiers to access the values, whereas unscoped enums disallow that. Next, in your class, just use a static constant member:
template<> struct jit_type<float> {
static constexpr value = jit_ptx_type::f32;
};
often I use big struct with more than 20 fields which need to be initialized by different values.
Every time I wrote the init function, I was hypochondriac, that I always worried about I miss one field to be assigned a value. So I had to check each field one by one.
I hate this, So I use a CHECK_VAL macro like sample code.
Now if i miss one item in the struct initialization, the compiler will report an error:
a value of type "Check" cannot be used to initialize an entity of type
"int"
My question: whether there are other way to help my problem? The language is C and C++, and the big struct is POD type.
Code Sample
#define DOCHECK 1
#if DOCHECK
typedef struct _Check{
char k;
} Check;
Check g_check = {'c'};
#define CHECK_DEL Check c1234567;
#define CHECK_VAL (g_check)
#else
#define CHECK_DEL
#define CHECK_VAL
#endif
typedef struct _BigStruct{
int bar;
int foo;
/*...*/
int f99;
int f100;
CHECK_DEL;
}BigStruct;
void initBigStruct(BigStruct* p){
int a,b,c,d;
a = b = c = d = 0;
/*
many other code to caculate the value of a,b,c,d
*/
{
BigStruct tmp = {a,b,c,d, CHECK_VAL};
*p = tmp;
}
}
From a language point of view, probably not a lot.
However, GCC has the -Wmissing-field-initializers flag for exactly this situation. I'm sure other compilers offer something similar.
If you are talking about C++, you could just write a constructor to the class, initializing with whatever you wanted. But ofcourse that would un-POD your data and prevent struct initialization with {..}.
If it is C, you can write a factory method which returns an initialized struct as #Pubby suggests.
If counting the number of variables is what is bothering you, you could use named initialization as in C struct initialization using labels. It works, but how? Documentation?
Many years after this question was posted, I faced the same problem with nested structs. I present an alternative answer which goes beyond the scope of POD structs. It does however solve the principal problem of making sure that all fields of a data hierarchy are assigned. The solutions uses std::optional and std::tuple and can handle nested data hierarchies.
Consider
struct Aggregate2 {
struct Aggregate1 {
int DataType1;
float DataType2;
} aggregate1
size_t DataType3;
} aggregate2;
and compare to
using Aggregate1 = std::tuple<std::optional<int>, std::optional<float>>;
using DataType3 = size_t;
std::tuple<std::optional<Aggregate1>, std::optional<DataType3>> aggregate2;
Technically this can store the desired data but the getting and setting will not be readable and the checking for assignment will not be automatic with ease.
These problems are arguably solved here with the trade-off that the definition of the types holding the hierarchy are not as readable as the struct way. The code below compiles with MSVC and gcc. It contains detailed instructions for how to use it.
//struct_alternative.h
#include <tuple>
#include <optional>
// C++17 template variable to determine if type is std::tuple.
template <typename T>
constexpr bool isTuple = false;
template<typename ... Types>
constexpr bool isTuple<std::tuple<Types...>> = true;
// Get last type of std::tuple
template<typename ...T>
using LastEntityType = std::tuple_element_t<sizeof...(T) - 1, std::tuple<T...>>;
// Class that inherits all members of D.
// Constructor parses that data tree and throws if any instance of D has unassigned Data::data.
template<typename D>
class AssignedData: public D {
public:
explicit AssignedData(D&& d) : D(std::move(d)) {
if constexpr (isTuple<typename decltype(D::data)::value_type>) {
std::apply([&](auto&&... args) {((args.throwIfNotAssigned()), ...);}, *d.data);
} else {
d.throwIfNotAssigned();
}
}
};
//
// Data is a template class with capability of storing a hierarchy (tree-like structure) of tuple data.
// The template argument represents the type of the data that is stored in an std::optional<T>
// It has a set and get functinality.
//
// Use as follows:
//
// Define data classes that inherit from Data.
//
// class DataType1 : public Data<int>{};
// class DataType2 : public Data<float>{};
//
// Construct aggregate data types where the template argumets can be a combination of tuples of previously
// defined data types and new data types.
//
// class DataType3 : public Data<size_t>{};
// class Aggregate1 : public Data<std::tuple<DataType1, DataType2>>
// class Aggregate2 : public Data<std::tuple::<Aggregate1, DataType3>>{};
//
// Create intsances of the Aggregate data type and assign the members.
//
// Arrgregate2 aggregate2;
// aggregate2.set<Aggregate1, DataType1>(1); // Will assigne the value 1 to DataType1 of aggregate2::Aggregate1.
//
// Create an AssignedData object that guarantees that all members are assigned.
//
// auto assigned = AssignedData(std::move(aggregate)); // Will throw when not all data members are assigned.
//
// Get data member through
//
// int dataType1 = assigned.get<DataType4, DataType1>;
//
template <typename T>
class Data {
public:
Data() {
if constexpr(isTuple<T>) {
// Make sure that all tuples are assigned.
// If not done, Data::data which is of type std::optional<std::tuple<A, B ...>>
// can get the tuple members (A, B ...) assigned but the std::optional<std::tuple<A, B...>>
// will not have a value i.e. is an empty std::optional. This becomes a problem when traversing the Data tree.
data = std::make_optional<T>();
}
}
// Throw if any member of Data::data is not assigned i.e. is an empty optional.
void throwIfNotAssigned() const {
if (data.has_value()) {
if constexpr (isTuple<T>) {
std::apply([&](auto&&... args) {((args.throwIfNotAssigned()), ...);}, *data);
}
} else {
throw(std::runtime_error("Data::data is not set."));
}
}
// Get value of the data type corresponding to the last element of (First, ...U)
template <typename First, typename ...U>
auto get() const {
if constexpr(isTuple<typename decltype(data)::value_type>) {
if constexpr (sizeof...(U) > 0) {
return std::get<First>(*data).template get<U...>();
} else if (std::get<First>(*data).data.has_value()){
return std::get<First>(*data).data.value();
}
} else if (data.has_value()) {
return data.value();
}
throw(std::runtime_error("Trying to get a Data::data that is not set."));
}
// Set value of the data type corresponding to the last element of (First, ...U)
template<typename First, typename ...U>
void set(const typename decltype(LastEntityType<First, U...>::data)::value_type& rhs) {
if constexpr(isTuple<typename decltype(data)::value_type>) {
if constexpr (sizeof...(U) > 0) {
std::get<First>(*data).template set<U...>(rhs);
} else
std::get<First>(*data).data = rhs;
} else {
data = rhs;
}
}
std::optional<T> data;
};
I have a mixture of C++ classes, some of which store a state variable 0..9 as integer, others use '0' ... '9'. Currently I do:
enum { kOne = '1' };
class StoresValueAsInt {
static int value; // contains 0 ... 9
};
class StoresValueAsChar {
static char value; // contains '0' ... '9'
};
class StoresValueAsChar {
static char value;
};
template <typename X>
isOne() { return X::value == kOne; }
This allows me to write the buggy code isOne<StoresValueAsInt::value>(). Instead, I would like to have the compiler complain about this incorrect usage. I tried the following:
enum ValueInt {
kOne = 1
};
enum ValueChar {
kOne = '1'
};
class StoresValueAsInt {
static ValueInt value;
};
class StoresValueAsChar {
static ValueChar value;
};
class StoresValueAsChar2 {
static ValueChar value;
};
However, this does not work, because kOne is visible at the namespace level, and thus it has conflicting declarations of kOne.
Is there a way to not have the enums declared in the namespace? Or a better approach here?
updated: Added what I currently do; hoping to clarify my use case.
As far as best practice, I'm really not sure what you are trying to accomplish here so I can't point you in the right direction.
However, to answer your question directly: Placing the enums in seperate namespaces would solve your issue:
namespace IntVals {
enum ValueInt {
kOne = 1
};
}
namespace CharVals {
enum ValueChar {
kOne = '1'
};
}
class StoresValueAsInt {
static ValueInt value;
};
class StoresValueAsChar {
static ValueChar value;
};
Then you should be able to use the following:
StoresValueAsInt::value == CharVals::kOne
Though this is ugly, and I wish I understood better what you were going for to offer a more elegant solution than this.
Is there a way to not have the enums declared in the namespace? Or a better approach here?
Yes, you can differentiate your enums as below:
struct ValueInt {
enum { // no need to have enum Name
kOne = 1
};
};
struct ValueChar {
enum { // no need to have enum Name
kOne = '1'
};
};
Usage:
ValueInt::kOne;
ValueChar::kOne;
I have this "better" enum class that
cannot contain invalid values, and
cannot be used until enum value is not set explicitly,
as follows:
class Symmetry
{
public:
enum Type {
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN, UNINITIALIZED
};
Symmetry() { t_ = UNINITIALIZED }
explicit Symmetry(Type t) : t_(t) { checkArg(t); }
Symmetry& operator=(Type t) { checkArg(t); t_ = t; return *this; }
operator Type() const {
if (t_ == UNINITIALIZED) throw runtime_error("error");
return t_;
}
private:
Type t_;
void checkArg(Type t) {
if ((unsigned)t >= (unsigned)UNINITIALIZED)
throw runtime_error("error");
}
};
This allows me to write the following code:
Symmetry s1(Symmetry::SYMMETRIC);
Symmetry s2;
s2 = Symmetry::HERMITIAN;
Symmetry s3;
if (Symmetry::GENERAL == s3) // throws
My problem is that a compiler allows constructs such as:
Symmetry s1((Symmetry::Type)18); // throws
Symmetry s2;
s2 = (Symmetry::Type)18; // throws
I solved this problem by throwing exceptions, but I would prefer such a code not to compile at all (a compile time error). Is there a way how to manage this?
Potentially a crummy solution, but it would solve your immediate problem. Rather than having an inner enum type, define a little helper class with a private constructor, and make the outer class a friend. Then the "enum" values can be static const members in your outer class. Something like this:
(DISCLAIMER: untested, so there may be various compilation issues, but you should get the idea)
class Symmetry
{
public:
class Type
{
private:
Type() {};
friend class Symmetry;
};
static const Type GENERAL;
static const Type SYMMETRIC;
static const Type HERMITIAN;
};
You will need some way of determining equality, but this should be fairly easy.
My attempt using templates: (tested. However, this can be further improved!)
template<int N>
struct Symmetry
{
enum Type
{
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
template<Type e> struct allowed;
template<> struct allowed<GENERAL> { static const int value = GENERAL; };
template<> struct allowed<SYMMETRIC> { static const int value = SYMMETRIC; };
template<> struct allowed<HERMITIAN> { static const int value = HERMITIAN; };
template<> struct allowed<SKEW_SYMMETRIC> { static const int value = SKEW_SYMMETRIC; };
template<> struct allowed<SKEW_HERMITIAN> { static const int value = SKEW_HERMITIAN; };
allowed<(Type)N> m_allowed;
operator int()
{
return N;
}
};
Symmetry<0> e0; //okay
Symmetry<1> e1; //okay
Symmetry<100> e4; //compilation error!
Symmetry<e0.SKEW_HERMITIAN> e3; //okay
Symmetry<e0.SKEW_SYMMETRIC> e3; //okay
Usage:
int main()
{
Symmetry<0> e0;
Symmetry<e0.HERMITIAN> e1;
switch (e1)
{
case e0.HERMITIAN:
{
cout << "It's working" << endl;
}
break;
}
}
No. If you allow any cast to be used, as your last example does, then there will always be some cast that can be used to subvert your type.
The solution is to not be in the habit of using these casts and to very suspiciously consider any code that uses these casts indiscriminately. View this type of casting as the nuclear bomb in your arsenal: it's important to have, but you always handle it with care and never want to deploy it more than rarely.
What warning options does your compiler have for casting? What lint tools are you using which may detect this misuse of casts?
That said, it appears you really want to hide the inner Type so users are less tempted to even use it. Realizing that, it's straight-forward to make that type name private, even while not preventing all cast misuse, by slightly tweaking your original:
struct Symmetry {
enum {
UNINITIALIZED,
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
private:
typedef decltype(UNINITIALIZED) Hidden;
Hidden _value;
public:
Symmetry(Hidden value = UNINITIALIZED) : _value (value) {}
Symmetry& operator=(Hidden value) { _value = value; return *this; }
operator Hidden() const {
if (_value == UNINITIALIZED) {
throw std::logic_error("uninitialized Symmetry");
}
return _value;
}
bool initialized() const { return _value != UNINITIALIZED; }
// required if you want to check for UNINITIALIZED without throwing in
// the above conversion
};
This is a complete implementation, no details omitted or unknown, or issues with initialization order. The only caveat is decltype – with a pre-C++0x compiler, you'll have to use something implementation-specific or a library which wraps something implementation-specific.
And a smaller issue: change from runtime_error to logic_error, as using uninitialized values should be preventable beforehand and thus falls in the latter category.