FlagSet with C++ templates
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
10
down vote
favorite
I am creating a C++ header-only library for FlagSet.
A FlagSet is a container for booleans with meaning. Usually, it is done with constants (macro, enums, whatever), for example :
//from SDL2
#define SDL_INIT_AUDIO 0x00000010u
#define SDL_INIT_VIDEO 0x00000020u
#define SDL_INIT_EVENTS 0X00000040u
...
Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO;
if ((flags & SDL_INIT_VIDEO))
/* video implies events */
flags
It is not as safe as a scoped enum (enum class
), you can mix up flags from one meaning to another (for example, it is possible to do SDL_INIT_AUDIO | SDL_WINDOW_RESIZABLE
even if they do not share any logic).
The aim of my FlagSet library is to provide a "scoped flags" (by analogy with scoped enum). Here is my code :
#include <iostream>
#include <cstddef>
#include <type_traits>
#include <string>
/////////// INDEX OF ///////////
template<typename S,typename...>
struct IndexOf_t ;
template<typename S, typename...Ts>
struct IndexOf_t<S,S,Ts...>
static constexpr std::size_t index = 0;
;
template<typename S, typename T, typename...Ts>
struct IndexOf_t<S,T,Ts...>
static constexpr std::size_t index = 1 + IndexOf_t<S,Ts...>::index;
;
template<typename Searching, typename...List>
constexpr std::size_t IndexOf = IndexOf_t<Searching,List...>::index;
////////////////////////////////
/////////// FLAGSET ////////////
template <typename U, typename...Fs>
struct FlagSet
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
constexpr FlagSet() : mData0 ;
template<typename...Ts>
static constexpr FlagSet<U,Fs...> Value()
return FlagSet Mask<Ts...> ;
;
template<typename...Ts>
constexpr void set()
mData ;
template<typename...Ts>
constexpr void reset()
mData &= ~Mask<Ts...>;
;
template<typename...Ts>
constexpr void toggle()
mData ^= Mask<Ts...>;
;
constexpr void setAll()
set<Fs...>();
;
constexpr void resetAll()
reset<Fs...>();
;
constexpr void toggleAll()
toggle<Fs...>();
;
template<typename...Ts>
constexpr bool anyOf() const
return (mData & Mask<Ts...>) != 0;
;
template<typename...Ts>
constexpr bool allOf() const
return (mData & Mask<Ts...>) == Mask<Ts...>;
;
template<typename...Ts>
constexpr bool noneOf() const
return (mData & Mask<Ts...>) == 0;
;
constexpr bool all() const return allOf<Fs...>(); ;
constexpr bool any() const return anyOf<Fs...>(); ;
constexpr bool none() const return noneOf<Fs...>(); ;
std::string to_string(char zero = '0', char one = '1') const
std::string str(sizeof...(Fs),zero);
std::size_t count = 0;
for (U data = mData; data != 0 && count < sizeof...(Fs); data >>= 1, ++count)
if (data & 1) str[count] = one;
return str;
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
private:
U mData;
constexpr FlagSet(U data) : mDatadata ;
template<typename...>
struct Mask_t
static constexpr U mask = 0;
;
template<typename T>
struct Mask_t<T>
static constexpr U mask = 1 << IndexOf<T,Fs...>;
;
template<typename T, typename...Ts>
struct Mask_t<T,Ts...>
static constexpr U mask = Mask_t<T>::mask ;
template<typename...Ts>
static constexpr U Mask = Mask_t<Ts...>::mask;
;
template<typename U, typename...Fs>
std::ostream& operator<<(std::ostream& os, FlagSet<U,Fs...> fs)
return os << fs.to_string();
;
////////////////////////////////
namespace Direction
struct UP;
struct DOWN;
struct LEFT;
struct RIGHT;
using Type = FlagSet<char, UP, DOWN, LEFT, RIGHT>;
;
int main()
using namespace std;
Direction::Type player_dir = Direction::Type::Value<Direction::DOWN>();
cout << "display : UP DOWN LEFT RIGHT " << endl;
cout << "player1 : " << player_dir << endl; // 0100
player_dir.set<Direction::LEFT>(); //now the player goes left too
cout << "player2 : " << player_dir << endl; // 0110
player_dir.resetAll(); //now the player goes nowhere
cout << "player3 : " << player_dir << endl; // 0000
player_dir.set<Direction::RIGHT>(); //now the player goes right
cout << "player4 : " << player_dir << endl; // 0001
player_dir.toggle<Direction::RIGHT,Direction::UP>(); //now the player goes only up
cout << "player5 : " << player_dir << endl; // 1000
if (player_dir.any()) //player is moving
cout << "player is moving !n"; //TRUE
if (player_dir.noneOf<Direction::LEFT,Direction::RIGHT>()) //player is not moving on x-axis
cout << "player not moving on x-axisn"; //TRUE
if (player_dir.noneOf<Direction::UP,Direction::DOWN>()) //player is not moving on y-axis
cout << "player not moving on y-axisn"; //FALSE
return 0;
;
Thank you for anyone reviewing my code !
c++ template-meta-programming bitset
add a comment |Â
up vote
10
down vote
favorite
I am creating a C++ header-only library for FlagSet.
A FlagSet is a container for booleans with meaning. Usually, it is done with constants (macro, enums, whatever), for example :
//from SDL2
#define SDL_INIT_AUDIO 0x00000010u
#define SDL_INIT_VIDEO 0x00000020u
#define SDL_INIT_EVENTS 0X00000040u
...
Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO;
if ((flags & SDL_INIT_VIDEO))
/* video implies events */
flags
It is not as safe as a scoped enum (enum class
), you can mix up flags from one meaning to another (for example, it is possible to do SDL_INIT_AUDIO | SDL_WINDOW_RESIZABLE
even if they do not share any logic).
The aim of my FlagSet library is to provide a "scoped flags" (by analogy with scoped enum). Here is my code :
#include <iostream>
#include <cstddef>
#include <type_traits>
#include <string>
/////////// INDEX OF ///////////
template<typename S,typename...>
struct IndexOf_t ;
template<typename S, typename...Ts>
struct IndexOf_t<S,S,Ts...>
static constexpr std::size_t index = 0;
;
template<typename S, typename T, typename...Ts>
struct IndexOf_t<S,T,Ts...>
static constexpr std::size_t index = 1 + IndexOf_t<S,Ts...>::index;
;
template<typename Searching, typename...List>
constexpr std::size_t IndexOf = IndexOf_t<Searching,List...>::index;
////////////////////////////////
/////////// FLAGSET ////////////
template <typename U, typename...Fs>
struct FlagSet
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
constexpr FlagSet() : mData0 ;
template<typename...Ts>
static constexpr FlagSet<U,Fs...> Value()
return FlagSet Mask<Ts...> ;
;
template<typename...Ts>
constexpr void set()
mData ;
template<typename...Ts>
constexpr void reset()
mData &= ~Mask<Ts...>;
;
template<typename...Ts>
constexpr void toggle()
mData ^= Mask<Ts...>;
;
constexpr void setAll()
set<Fs...>();
;
constexpr void resetAll()
reset<Fs...>();
;
constexpr void toggleAll()
toggle<Fs...>();
;
template<typename...Ts>
constexpr bool anyOf() const
return (mData & Mask<Ts...>) != 0;
;
template<typename...Ts>
constexpr bool allOf() const
return (mData & Mask<Ts...>) == Mask<Ts...>;
;
template<typename...Ts>
constexpr bool noneOf() const
return (mData & Mask<Ts...>) == 0;
;
constexpr bool all() const return allOf<Fs...>(); ;
constexpr bool any() const return anyOf<Fs...>(); ;
constexpr bool none() const return noneOf<Fs...>(); ;
std::string to_string(char zero = '0', char one = '1') const
std::string str(sizeof...(Fs),zero);
std::size_t count = 0;
for (U data = mData; data != 0 && count < sizeof...(Fs); data >>= 1, ++count)
if (data & 1) str[count] = one;
return str;
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
private:
U mData;
constexpr FlagSet(U data) : mDatadata ;
template<typename...>
struct Mask_t
static constexpr U mask = 0;
;
template<typename T>
struct Mask_t<T>
static constexpr U mask = 1 << IndexOf<T,Fs...>;
;
template<typename T, typename...Ts>
struct Mask_t<T,Ts...>
static constexpr U mask = Mask_t<T>::mask ;
template<typename...Ts>
static constexpr U Mask = Mask_t<Ts...>::mask;
;
template<typename U, typename...Fs>
std::ostream& operator<<(std::ostream& os, FlagSet<U,Fs...> fs)
return os << fs.to_string();
;
////////////////////////////////
namespace Direction
struct UP;
struct DOWN;
struct LEFT;
struct RIGHT;
using Type = FlagSet<char, UP, DOWN, LEFT, RIGHT>;
;
int main()
using namespace std;
Direction::Type player_dir = Direction::Type::Value<Direction::DOWN>();
cout << "display : UP DOWN LEFT RIGHT " << endl;
cout << "player1 : " << player_dir << endl; // 0100
player_dir.set<Direction::LEFT>(); //now the player goes left too
cout << "player2 : " << player_dir << endl; // 0110
player_dir.resetAll(); //now the player goes nowhere
cout << "player3 : " << player_dir << endl; // 0000
player_dir.set<Direction::RIGHT>(); //now the player goes right
cout << "player4 : " << player_dir << endl; // 0001
player_dir.toggle<Direction::RIGHT,Direction::UP>(); //now the player goes only up
cout << "player5 : " << player_dir << endl; // 1000
if (player_dir.any()) //player is moving
cout << "player is moving !n"; //TRUE
if (player_dir.noneOf<Direction::LEFT,Direction::RIGHT>()) //player is not moving on x-axis
cout << "player not moving on x-axisn"; //TRUE
if (player_dir.noneOf<Direction::UP,Direction::DOWN>()) //player is not moving on y-axis
cout << "player not moving on y-axisn"; //FALSE
return 0;
;
Thank you for anyone reviewing my code !
c++ template-meta-programming bitset
You might be interested in this: gist.github.com/makulik/7963331
â ÃÂìýÃÂñ á¿¥Ã栨Â
Jun 19 at 22:09
This might also be interesting: codereview.stackexchange.com/questions/183246/â¦
â Cris Luengo
Jun 20 at 1:22
add a comment |Â
up vote
10
down vote
favorite
up vote
10
down vote
favorite
I am creating a C++ header-only library for FlagSet.
A FlagSet is a container for booleans with meaning. Usually, it is done with constants (macro, enums, whatever), for example :
//from SDL2
#define SDL_INIT_AUDIO 0x00000010u
#define SDL_INIT_VIDEO 0x00000020u
#define SDL_INIT_EVENTS 0X00000040u
...
Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO;
if ((flags & SDL_INIT_VIDEO))
/* video implies events */
flags
It is not as safe as a scoped enum (enum class
), you can mix up flags from one meaning to another (for example, it is possible to do SDL_INIT_AUDIO | SDL_WINDOW_RESIZABLE
even if they do not share any logic).
The aim of my FlagSet library is to provide a "scoped flags" (by analogy with scoped enum). Here is my code :
#include <iostream>
#include <cstddef>
#include <type_traits>
#include <string>
/////////// INDEX OF ///////////
template<typename S,typename...>
struct IndexOf_t ;
template<typename S, typename...Ts>
struct IndexOf_t<S,S,Ts...>
static constexpr std::size_t index = 0;
;
template<typename S, typename T, typename...Ts>
struct IndexOf_t<S,T,Ts...>
static constexpr std::size_t index = 1 + IndexOf_t<S,Ts...>::index;
;
template<typename Searching, typename...List>
constexpr std::size_t IndexOf = IndexOf_t<Searching,List...>::index;
////////////////////////////////
/////////// FLAGSET ////////////
template <typename U, typename...Fs>
struct FlagSet
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
constexpr FlagSet() : mData0 ;
template<typename...Ts>
static constexpr FlagSet<U,Fs...> Value()
return FlagSet Mask<Ts...> ;
;
template<typename...Ts>
constexpr void set()
mData ;
template<typename...Ts>
constexpr void reset()
mData &= ~Mask<Ts...>;
;
template<typename...Ts>
constexpr void toggle()
mData ^= Mask<Ts...>;
;
constexpr void setAll()
set<Fs...>();
;
constexpr void resetAll()
reset<Fs...>();
;
constexpr void toggleAll()
toggle<Fs...>();
;
template<typename...Ts>
constexpr bool anyOf() const
return (mData & Mask<Ts...>) != 0;
;
template<typename...Ts>
constexpr bool allOf() const
return (mData & Mask<Ts...>) == Mask<Ts...>;
;
template<typename...Ts>
constexpr bool noneOf() const
return (mData & Mask<Ts...>) == 0;
;
constexpr bool all() const return allOf<Fs...>(); ;
constexpr bool any() const return anyOf<Fs...>(); ;
constexpr bool none() const return noneOf<Fs...>(); ;
std::string to_string(char zero = '0', char one = '1') const
std::string str(sizeof...(Fs),zero);
std::size_t count = 0;
for (U data = mData; data != 0 && count < sizeof...(Fs); data >>= 1, ++count)
if (data & 1) str[count] = one;
return str;
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
private:
U mData;
constexpr FlagSet(U data) : mDatadata ;
template<typename...>
struct Mask_t
static constexpr U mask = 0;
;
template<typename T>
struct Mask_t<T>
static constexpr U mask = 1 << IndexOf<T,Fs...>;
;
template<typename T, typename...Ts>
struct Mask_t<T,Ts...>
static constexpr U mask = Mask_t<T>::mask ;
template<typename...Ts>
static constexpr U Mask = Mask_t<Ts...>::mask;
;
template<typename U, typename...Fs>
std::ostream& operator<<(std::ostream& os, FlagSet<U,Fs...> fs)
return os << fs.to_string();
;
////////////////////////////////
namespace Direction
struct UP;
struct DOWN;
struct LEFT;
struct RIGHT;
using Type = FlagSet<char, UP, DOWN, LEFT, RIGHT>;
;
int main()
using namespace std;
Direction::Type player_dir = Direction::Type::Value<Direction::DOWN>();
cout << "display : UP DOWN LEFT RIGHT " << endl;
cout << "player1 : " << player_dir << endl; // 0100
player_dir.set<Direction::LEFT>(); //now the player goes left too
cout << "player2 : " << player_dir << endl; // 0110
player_dir.resetAll(); //now the player goes nowhere
cout << "player3 : " << player_dir << endl; // 0000
player_dir.set<Direction::RIGHT>(); //now the player goes right
cout << "player4 : " << player_dir << endl; // 0001
player_dir.toggle<Direction::RIGHT,Direction::UP>(); //now the player goes only up
cout << "player5 : " << player_dir << endl; // 1000
if (player_dir.any()) //player is moving
cout << "player is moving !n"; //TRUE
if (player_dir.noneOf<Direction::LEFT,Direction::RIGHT>()) //player is not moving on x-axis
cout << "player not moving on x-axisn"; //TRUE
if (player_dir.noneOf<Direction::UP,Direction::DOWN>()) //player is not moving on y-axis
cout << "player not moving on y-axisn"; //FALSE
return 0;
;
Thank you for anyone reviewing my code !
c++ template-meta-programming bitset
I am creating a C++ header-only library for FlagSet.
A FlagSet is a container for booleans with meaning. Usually, it is done with constants (macro, enums, whatever), for example :
//from SDL2
#define SDL_INIT_AUDIO 0x00000010u
#define SDL_INIT_VIDEO 0x00000020u
#define SDL_INIT_EVENTS 0X00000040u
...
Uint32 flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO;
if ((flags & SDL_INIT_VIDEO))
/* video implies events */
flags
It is not as safe as a scoped enum (enum class
), you can mix up flags from one meaning to another (for example, it is possible to do SDL_INIT_AUDIO | SDL_WINDOW_RESIZABLE
even if they do not share any logic).
The aim of my FlagSet library is to provide a "scoped flags" (by analogy with scoped enum). Here is my code :
#include <iostream>
#include <cstddef>
#include <type_traits>
#include <string>
/////////// INDEX OF ///////////
template<typename S,typename...>
struct IndexOf_t ;
template<typename S, typename...Ts>
struct IndexOf_t<S,S,Ts...>
static constexpr std::size_t index = 0;
;
template<typename S, typename T, typename...Ts>
struct IndexOf_t<S,T,Ts...>
static constexpr std::size_t index = 1 + IndexOf_t<S,Ts...>::index;
;
template<typename Searching, typename...List>
constexpr std::size_t IndexOf = IndexOf_t<Searching,List...>::index;
////////////////////////////////
/////////// FLAGSET ////////////
template <typename U, typename...Fs>
struct FlagSet
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
constexpr FlagSet() : mData0 ;
template<typename...Ts>
static constexpr FlagSet<U,Fs...> Value()
return FlagSet Mask<Ts...> ;
;
template<typename...Ts>
constexpr void set()
mData ;
template<typename...Ts>
constexpr void reset()
mData &= ~Mask<Ts...>;
;
template<typename...Ts>
constexpr void toggle()
mData ^= Mask<Ts...>;
;
constexpr void setAll()
set<Fs...>();
;
constexpr void resetAll()
reset<Fs...>();
;
constexpr void toggleAll()
toggle<Fs...>();
;
template<typename...Ts>
constexpr bool anyOf() const
return (mData & Mask<Ts...>) != 0;
;
template<typename...Ts>
constexpr bool allOf() const
return (mData & Mask<Ts...>) == Mask<Ts...>;
;
template<typename...Ts>
constexpr bool noneOf() const
return (mData & Mask<Ts...>) == 0;
;
constexpr bool all() const return allOf<Fs...>(); ;
constexpr bool any() const return anyOf<Fs...>(); ;
constexpr bool none() const return noneOf<Fs...>(); ;
std::string to_string(char zero = '0', char one = '1') const
std::string str(sizeof...(Fs),zero);
std::size_t count = 0;
for (U data = mData; data != 0 && count < sizeof...(Fs); data >>= 1, ++count)
if (data & 1) str[count] = one;
return str;
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
private:
U mData;
constexpr FlagSet(U data) : mDatadata ;
template<typename...>
struct Mask_t
static constexpr U mask = 0;
;
template<typename T>
struct Mask_t<T>
static constexpr U mask = 1 << IndexOf<T,Fs...>;
;
template<typename T, typename...Ts>
struct Mask_t<T,Ts...>
static constexpr U mask = Mask_t<T>::mask ;
template<typename...Ts>
static constexpr U Mask = Mask_t<Ts...>::mask;
;
template<typename U, typename...Fs>
std::ostream& operator<<(std::ostream& os, FlagSet<U,Fs...> fs)
return os << fs.to_string();
;
////////////////////////////////
namespace Direction
struct UP;
struct DOWN;
struct LEFT;
struct RIGHT;
using Type = FlagSet<char, UP, DOWN, LEFT, RIGHT>;
;
int main()
using namespace std;
Direction::Type player_dir = Direction::Type::Value<Direction::DOWN>();
cout << "display : UP DOWN LEFT RIGHT " << endl;
cout << "player1 : " << player_dir << endl; // 0100
player_dir.set<Direction::LEFT>(); //now the player goes left too
cout << "player2 : " << player_dir << endl; // 0110
player_dir.resetAll(); //now the player goes nowhere
cout << "player3 : " << player_dir << endl; // 0000
player_dir.set<Direction::RIGHT>(); //now the player goes right
cout << "player4 : " << player_dir << endl; // 0001
player_dir.toggle<Direction::RIGHT,Direction::UP>(); //now the player goes only up
cout << "player5 : " << player_dir << endl; // 1000
if (player_dir.any()) //player is moving
cout << "player is moving !n"; //TRUE
if (player_dir.noneOf<Direction::LEFT,Direction::RIGHT>()) //player is not moving on x-axis
cout << "player not moving on x-axisn"; //TRUE
if (player_dir.noneOf<Direction::UP,Direction::DOWN>()) //player is not moving on y-axis
cout << "player not moving on y-axisn"; //FALSE
return 0;
;
Thank you for anyone reviewing my code !
c++ template-meta-programming bitset
asked Jun 19 at 21:46
Julien Vernay
3138
3138
You might be interested in this: gist.github.com/makulik/7963331
â ÃÂìýÃÂñ á¿¥Ã栨Â
Jun 19 at 22:09
This might also be interesting: codereview.stackexchange.com/questions/183246/â¦
â Cris Luengo
Jun 20 at 1:22
add a comment |Â
You might be interested in this: gist.github.com/makulik/7963331
â ÃÂìýÃÂñ á¿¥Ã栨Â
Jun 19 at 22:09
This might also be interesting: codereview.stackexchange.com/questions/183246/â¦
â Cris Luengo
Jun 20 at 1:22
You might be interested in this: gist.github.com/makulik/7963331
â ÃÂìýÃÂñ á¿¥Ã栨Â
Jun 19 at 22:09
You might be interested in this: gist.github.com/makulik/7963331
â ÃÂìýÃÂñ á¿¥Ã栨Â
Jun 19 at 22:09
This might also be interesting: codereview.stackexchange.com/questions/183246/â¦
â Cris Luengo
Jun 20 at 1:22
This might also be interesting: codereview.stackexchange.com/questions/183246/â¦
â Cris Luengo
Jun 20 at 1:22
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
4
down vote
accepted
It's a great idea to provide a type-safe bit-mask, I like your idea a lot. That said, I feel like your code is in great need of modernization and simplification.
Recursion through inheritance is now a testimony to the ingenuity of our great predecessors, but it's time to get acquainted with if constexpr
and fold expressions
. I agree that you'll need C++17, but it's 2018 after all. For instance, here's your index_of
as a two-liner:
#include <type_traits>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>(); // doesn't have to be well-formed if U == V
N.B: As a function qualifier, constexpr
means that, given compile-time arguments, the function will be executed at compile-time.
In the same spirit, you can simplify Mask
:
template <typename U, typename... Ts>
struct Flag_set
// ...
template <typename... Us>
constexpr std::size_t mask() ...); // fold expression
// ...
U flags;
;
((1 << index_of<Us, Ts...>()) | ...)
will be expanded as:
(1 << index_of<Us1, Ts...>())
| (1 << index_of<Us2, Ts...>())
| ...
| (1 << index_of<UsN, Ts...>()))
Yet another function you can simplify, to_string()
:
template <typename U, typename... Ts>
struct Flag_set
// ...
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...); // fold
std::reverse(result.begin(), result.end());
return result;
// ...
U flags;
;
Here, the fold expression is expanded around the comma operator:
result.push_back(mask<Ts1>() & flags ? '1' : '0'),
result.push_back(mask<Ts2>() & flags ? '1' : '0'),
...
result.push_back(mask<TsN>() & flags ? '1' : '0');
Here's a minimal working example you can toy with and enhance with static assertions and such:
#include <string>
#include <iostream>
#include <type_traits>
#include <algorithm>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>();
template <typename U, typename... Ts>
struct Flag_set
Flag_set() : flags(0)
template <typename... Us>
constexpr std::size_t mask() ...);
template <typename... Us>
constexpr void set() = mask<Us...>();
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...);
std::reverse(result.begin(), result.end());
return result;
U flags;
;
int main()
Flag_set<std::size_t, char, int, float, double> fs;
std::cout << fs.mask<char, float>() << 'n';
fs.set<int, double>();
std::cout << fs.to_string() << 'n';
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !if constexpr
is really something I will use.
â Julien Vernay
Jun 21 at 7:07
add a comment |Â
up vote
10
down vote
IndexOf
can probably be a nested type of FlagSet
, unless you intend for that to be part of the public interface.
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
Are you sure SFINAE is what you want to do here? It would probably make more sense to static_assert
that U
is integral.
At the same time, you might also want to static assert that U
is unsigned, because you're doing bit twiddling, which is a dodgy prospect with signed types.
And also at the same time, you could static assert that sizeof...(Fs)
is less than or equal to the number of bits in U
.
Even better, perhaps, would be to static assert that U
is an unsigned, possibly fundamental, integral type or a std::bitset
or something else with the bit-twiddling interface you need.
Most every function could also probably benefit from being noexcept
.
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
This doesn't really need to be a friend, because it's only using the public interface.
What I would suggest, though, is a function that writes the string form of the flag set into a buffer. Something like:
template <typename CharT>
constexpr auto write(CharT* buffer, std::size_t size, CharT zero = CharT('0'), CharT one = CharT('1')) noexcept
// write zero and one into buffer[0] through
// buffer[min(size, sizeof...(Fs)], basically as you do in to_string
With that function, you could implement both to_string()
and the insertion operator:
auto to_string(char zero = '0', char one = '1') const
auto s = std::string(sizeof...(fs), zero);
write(s.data(), s.size(), zero, one);
return s;
template <typename CharT, typename Traits, typename U, typename... Fs>
auto operator<<(std::basic_ostream<CharT, Traits>& o, FlagSet<U, Fs...> fs) ->
std::basic_ostream<CharT, Traits>&
auto buf = std::array<CharT, sizeof...(Fs)>;
write(buf.data(), buf.size());
o.write(buf.data(), buf.size());
return o;
And you'll even get support for streams of other character types, too.
The only other thing I can think off of the top of my head is that you'll probably want operator==
and !=
. And maybe a way to set the flag set from either a bag o' bits (like an unsigned type or std::bitset
) or by reading a string (as in, the complement of operator<<
). Because otherwise, setting the flags using data read from a config file (for example) would be a pain.
Would be great to stress on importance of usingstatic_assert
here, as it is detrimental for useful error messages. +1 anyway, though.
â Incomputable
Jun 20 at 4:21
Thanks for your feedback, static_assert is something I will use ! For thewrite
function, why not even if I don't like the(raw pointer, size)
pattern (I consider using GSL::span ?). Usingoperator<<
would be great, as you say for serialization it would be useful.
â Julien Vernay
Jun 21 at 7:03
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
It's a great idea to provide a type-safe bit-mask, I like your idea a lot. That said, I feel like your code is in great need of modernization and simplification.
Recursion through inheritance is now a testimony to the ingenuity of our great predecessors, but it's time to get acquainted with if constexpr
and fold expressions
. I agree that you'll need C++17, but it's 2018 after all. For instance, here's your index_of
as a two-liner:
#include <type_traits>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>(); // doesn't have to be well-formed if U == V
N.B: As a function qualifier, constexpr
means that, given compile-time arguments, the function will be executed at compile-time.
In the same spirit, you can simplify Mask
:
template <typename U, typename... Ts>
struct Flag_set
// ...
template <typename... Us>
constexpr std::size_t mask() ...); // fold expression
// ...
U flags;
;
((1 << index_of<Us, Ts...>()) | ...)
will be expanded as:
(1 << index_of<Us1, Ts...>())
| (1 << index_of<Us2, Ts...>())
| ...
| (1 << index_of<UsN, Ts...>()))
Yet another function you can simplify, to_string()
:
template <typename U, typename... Ts>
struct Flag_set
// ...
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...); // fold
std::reverse(result.begin(), result.end());
return result;
// ...
U flags;
;
Here, the fold expression is expanded around the comma operator:
result.push_back(mask<Ts1>() & flags ? '1' : '0'),
result.push_back(mask<Ts2>() & flags ? '1' : '0'),
...
result.push_back(mask<TsN>() & flags ? '1' : '0');
Here's a minimal working example you can toy with and enhance with static assertions and such:
#include <string>
#include <iostream>
#include <type_traits>
#include <algorithm>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>();
template <typename U, typename... Ts>
struct Flag_set
Flag_set() : flags(0)
template <typename... Us>
constexpr std::size_t mask() ...);
template <typename... Us>
constexpr void set() = mask<Us...>();
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...);
std::reverse(result.begin(), result.end());
return result;
U flags;
;
int main()
Flag_set<std::size_t, char, int, float, double> fs;
std::cout << fs.mask<char, float>() << 'n';
fs.set<int, double>();
std::cout << fs.to_string() << 'n';
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !if constexpr
is really something I will use.
â Julien Vernay
Jun 21 at 7:07
add a comment |Â
up vote
4
down vote
accepted
It's a great idea to provide a type-safe bit-mask, I like your idea a lot. That said, I feel like your code is in great need of modernization and simplification.
Recursion through inheritance is now a testimony to the ingenuity of our great predecessors, but it's time to get acquainted with if constexpr
and fold expressions
. I agree that you'll need C++17, but it's 2018 after all. For instance, here's your index_of
as a two-liner:
#include <type_traits>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>(); // doesn't have to be well-formed if U == V
N.B: As a function qualifier, constexpr
means that, given compile-time arguments, the function will be executed at compile-time.
In the same spirit, you can simplify Mask
:
template <typename U, typename... Ts>
struct Flag_set
// ...
template <typename... Us>
constexpr std::size_t mask() ...); // fold expression
// ...
U flags;
;
((1 << index_of<Us, Ts...>()) | ...)
will be expanded as:
(1 << index_of<Us1, Ts...>())
| (1 << index_of<Us2, Ts...>())
| ...
| (1 << index_of<UsN, Ts...>()))
Yet another function you can simplify, to_string()
:
template <typename U, typename... Ts>
struct Flag_set
// ...
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...); // fold
std::reverse(result.begin(), result.end());
return result;
// ...
U flags;
;
Here, the fold expression is expanded around the comma operator:
result.push_back(mask<Ts1>() & flags ? '1' : '0'),
result.push_back(mask<Ts2>() & flags ? '1' : '0'),
...
result.push_back(mask<TsN>() & flags ? '1' : '0');
Here's a minimal working example you can toy with and enhance with static assertions and such:
#include <string>
#include <iostream>
#include <type_traits>
#include <algorithm>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>();
template <typename U, typename... Ts>
struct Flag_set
Flag_set() : flags(0)
template <typename... Us>
constexpr std::size_t mask() ...);
template <typename... Us>
constexpr void set() = mask<Us...>();
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...);
std::reverse(result.begin(), result.end());
return result;
U flags;
;
int main()
Flag_set<std::size_t, char, int, float, double> fs;
std::cout << fs.mask<char, float>() << 'n';
fs.set<int, double>();
std::cout << fs.to_string() << 'n';
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !if constexpr
is really something I will use.
â Julien Vernay
Jun 21 at 7:07
add a comment |Â
up vote
4
down vote
accepted
up vote
4
down vote
accepted
It's a great idea to provide a type-safe bit-mask, I like your idea a lot. That said, I feel like your code is in great need of modernization and simplification.
Recursion through inheritance is now a testimony to the ingenuity of our great predecessors, but it's time to get acquainted with if constexpr
and fold expressions
. I agree that you'll need C++17, but it's 2018 after all. For instance, here's your index_of
as a two-liner:
#include <type_traits>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>(); // doesn't have to be well-formed if U == V
N.B: As a function qualifier, constexpr
means that, given compile-time arguments, the function will be executed at compile-time.
In the same spirit, you can simplify Mask
:
template <typename U, typename... Ts>
struct Flag_set
// ...
template <typename... Us>
constexpr std::size_t mask() ...); // fold expression
// ...
U flags;
;
((1 << index_of<Us, Ts...>()) | ...)
will be expanded as:
(1 << index_of<Us1, Ts...>())
| (1 << index_of<Us2, Ts...>())
| ...
| (1 << index_of<UsN, Ts...>()))
Yet another function you can simplify, to_string()
:
template <typename U, typename... Ts>
struct Flag_set
// ...
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...); // fold
std::reverse(result.begin(), result.end());
return result;
// ...
U flags;
;
Here, the fold expression is expanded around the comma operator:
result.push_back(mask<Ts1>() & flags ? '1' : '0'),
result.push_back(mask<Ts2>() & flags ? '1' : '0'),
...
result.push_back(mask<TsN>() & flags ? '1' : '0');
Here's a minimal working example you can toy with and enhance with static assertions and such:
#include <string>
#include <iostream>
#include <type_traits>
#include <algorithm>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>();
template <typename U, typename... Ts>
struct Flag_set
Flag_set() : flags(0)
template <typename... Us>
constexpr std::size_t mask() ...);
template <typename... Us>
constexpr void set() = mask<Us...>();
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...);
std::reverse(result.begin(), result.end());
return result;
U flags;
;
int main()
Flag_set<std::size_t, char, int, float, double> fs;
std::cout << fs.mask<char, float>() << 'n';
fs.set<int, double>();
std::cout << fs.to_string() << 'n';
It's a great idea to provide a type-safe bit-mask, I like your idea a lot. That said, I feel like your code is in great need of modernization and simplification.
Recursion through inheritance is now a testimony to the ingenuity of our great predecessors, but it's time to get acquainted with if constexpr
and fold expressions
. I agree that you'll need C++17, but it's 2018 after all. For instance, here's your index_of
as a two-liner:
#include <type_traits>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>(); // doesn't have to be well-formed if U == V
N.B: As a function qualifier, constexpr
means that, given compile-time arguments, the function will be executed at compile-time.
In the same spirit, you can simplify Mask
:
template <typename U, typename... Ts>
struct Flag_set
// ...
template <typename... Us>
constexpr std::size_t mask() ...); // fold expression
// ...
U flags;
;
((1 << index_of<Us, Ts...>()) | ...)
will be expanded as:
(1 << index_of<Us1, Ts...>())
| (1 << index_of<Us2, Ts...>())
| ...
| (1 << index_of<UsN, Ts...>()))
Yet another function you can simplify, to_string()
:
template <typename U, typename... Ts>
struct Flag_set
// ...
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...); // fold
std::reverse(result.begin(), result.end());
return result;
// ...
U flags;
;
Here, the fold expression is expanded around the comma operator:
result.push_back(mask<Ts1>() & flags ? '1' : '0'),
result.push_back(mask<Ts2>() & flags ? '1' : '0'),
...
result.push_back(mask<TsN>() & flags ? '1' : '0');
Here's a minimal working example you can toy with and enhance with static assertions and such:
#include <string>
#include <iostream>
#include <type_traits>
#include <algorithm>
template <typename T, typename U, typename... Ts>
constexpr std::size_t index_of()
if constexpr (std::is_same_v<T, U>) return 0;
else return 1 + index_of<T, Ts...>();
template <typename U, typename... Ts>
struct Flag_set
Flag_set() : flags(0)
template <typename... Us>
constexpr std::size_t mask() ...);
template <typename... Us>
constexpr void set() = mask<Us...>();
std::string to_string()
std::string result;
(result.push_back(mask<Ts>() & flags ? '1' : '0'), ...);
std::reverse(result.begin(), result.end());
return result;
U flags;
;
int main()
Flag_set<std::size_t, char, int, float, double> fs;
std::cout << fs.mask<char, float>() << 'n';
fs.set<int, double>();
std::cout << fs.to_string() << 'n';
answered Jun 20 at 14:28
papagaga
2,514115
2,514115
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !if constexpr
is really something I will use.
â Julien Vernay
Jun 21 at 7:07
add a comment |Â
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !if constexpr
is really something I will use.
â Julien Vernay
Jun 21 at 7:07
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !
if constexpr
is really something I will use.â Julien Vernay
Jun 21 at 7:07
Thank you for your answer ! I don't fear C++17, indeed I want to master it, so your answer is really helping me ! I heard about fold expressions but I don't really get it, so thanks for providing me a minimal working example !
if constexpr
is really something I will use.â Julien Vernay
Jun 21 at 7:07
add a comment |Â
up vote
10
down vote
IndexOf
can probably be a nested type of FlagSet
, unless you intend for that to be part of the public interface.
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
Are you sure SFINAE is what you want to do here? It would probably make more sense to static_assert
that U
is integral.
At the same time, you might also want to static assert that U
is unsigned, because you're doing bit twiddling, which is a dodgy prospect with signed types.
And also at the same time, you could static assert that sizeof...(Fs)
is less than or equal to the number of bits in U
.
Even better, perhaps, would be to static assert that U
is an unsigned, possibly fundamental, integral type or a std::bitset
or something else with the bit-twiddling interface you need.
Most every function could also probably benefit from being noexcept
.
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
This doesn't really need to be a friend, because it's only using the public interface.
What I would suggest, though, is a function that writes the string form of the flag set into a buffer. Something like:
template <typename CharT>
constexpr auto write(CharT* buffer, std::size_t size, CharT zero = CharT('0'), CharT one = CharT('1')) noexcept
// write zero and one into buffer[0] through
// buffer[min(size, sizeof...(Fs)], basically as you do in to_string
With that function, you could implement both to_string()
and the insertion operator:
auto to_string(char zero = '0', char one = '1') const
auto s = std::string(sizeof...(fs), zero);
write(s.data(), s.size(), zero, one);
return s;
template <typename CharT, typename Traits, typename U, typename... Fs>
auto operator<<(std::basic_ostream<CharT, Traits>& o, FlagSet<U, Fs...> fs) ->
std::basic_ostream<CharT, Traits>&
auto buf = std::array<CharT, sizeof...(Fs)>;
write(buf.data(), buf.size());
o.write(buf.data(), buf.size());
return o;
And you'll even get support for streams of other character types, too.
The only other thing I can think off of the top of my head is that you'll probably want operator==
and !=
. And maybe a way to set the flag set from either a bag o' bits (like an unsigned type or std::bitset
) or by reading a string (as in, the complement of operator<<
). Because otherwise, setting the flags using data read from a config file (for example) would be a pain.
Would be great to stress on importance of usingstatic_assert
here, as it is detrimental for useful error messages. +1 anyway, though.
â Incomputable
Jun 20 at 4:21
Thanks for your feedback, static_assert is something I will use ! For thewrite
function, why not even if I don't like the(raw pointer, size)
pattern (I consider using GSL::span ?). Usingoperator<<
would be great, as you say for serialization it would be useful.
â Julien Vernay
Jun 21 at 7:03
add a comment |Â
up vote
10
down vote
IndexOf
can probably be a nested type of FlagSet
, unless you intend for that to be part of the public interface.
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
Are you sure SFINAE is what you want to do here? It would probably make more sense to static_assert
that U
is integral.
At the same time, you might also want to static assert that U
is unsigned, because you're doing bit twiddling, which is a dodgy prospect with signed types.
And also at the same time, you could static assert that sizeof...(Fs)
is less than or equal to the number of bits in U
.
Even better, perhaps, would be to static assert that U
is an unsigned, possibly fundamental, integral type or a std::bitset
or something else with the bit-twiddling interface you need.
Most every function could also probably benefit from being noexcept
.
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
This doesn't really need to be a friend, because it's only using the public interface.
What I would suggest, though, is a function that writes the string form of the flag set into a buffer. Something like:
template <typename CharT>
constexpr auto write(CharT* buffer, std::size_t size, CharT zero = CharT('0'), CharT one = CharT('1')) noexcept
// write zero and one into buffer[0] through
// buffer[min(size, sizeof...(Fs)], basically as you do in to_string
With that function, you could implement both to_string()
and the insertion operator:
auto to_string(char zero = '0', char one = '1') const
auto s = std::string(sizeof...(fs), zero);
write(s.data(), s.size(), zero, one);
return s;
template <typename CharT, typename Traits, typename U, typename... Fs>
auto operator<<(std::basic_ostream<CharT, Traits>& o, FlagSet<U, Fs...> fs) ->
std::basic_ostream<CharT, Traits>&
auto buf = std::array<CharT, sizeof...(Fs)>;
write(buf.data(), buf.size());
o.write(buf.data(), buf.size());
return o;
And you'll even get support for streams of other character types, too.
The only other thing I can think off of the top of my head is that you'll probably want operator==
and !=
. And maybe a way to set the flag set from either a bag o' bits (like an unsigned type or std::bitset
) or by reading a string (as in, the complement of operator<<
). Because otherwise, setting the flags using data read from a config file (for example) would be a pain.
Would be great to stress on importance of usingstatic_assert
here, as it is detrimental for useful error messages. +1 anyway, though.
â Incomputable
Jun 20 at 4:21
Thanks for your feedback, static_assert is something I will use ! For thewrite
function, why not even if I don't like the(raw pointer, size)
pattern (I consider using GSL::span ?). Usingoperator<<
would be great, as you say for serialization it would be useful.
â Julien Vernay
Jun 21 at 7:03
add a comment |Â
up vote
10
down vote
up vote
10
down vote
IndexOf
can probably be a nested type of FlagSet
, unless you intend for that to be part of the public interface.
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
Are you sure SFINAE is what you want to do here? It would probably make more sense to static_assert
that U
is integral.
At the same time, you might also want to static assert that U
is unsigned, because you're doing bit twiddling, which is a dodgy prospect with signed types.
And also at the same time, you could static assert that sizeof...(Fs)
is less than or equal to the number of bits in U
.
Even better, perhaps, would be to static assert that U
is an unsigned, possibly fundamental, integral type or a std::bitset
or something else with the bit-twiddling interface you need.
Most every function could also probably benefit from being noexcept
.
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
This doesn't really need to be a friend, because it's only using the public interface.
What I would suggest, though, is a function that writes the string form of the flag set into a buffer. Something like:
template <typename CharT>
constexpr auto write(CharT* buffer, std::size_t size, CharT zero = CharT('0'), CharT one = CharT('1')) noexcept
// write zero and one into buffer[0] through
// buffer[min(size, sizeof...(Fs)], basically as you do in to_string
With that function, you could implement both to_string()
and the insertion operator:
auto to_string(char zero = '0', char one = '1') const
auto s = std::string(sizeof...(fs), zero);
write(s.data(), s.size(), zero, one);
return s;
template <typename CharT, typename Traits, typename U, typename... Fs>
auto operator<<(std::basic_ostream<CharT, Traits>& o, FlagSet<U, Fs...> fs) ->
std::basic_ostream<CharT, Traits>&
auto buf = std::array<CharT, sizeof...(Fs)>;
write(buf.data(), buf.size());
o.write(buf.data(), buf.size());
return o;
And you'll even get support for streams of other character types, too.
The only other thing I can think off of the top of my head is that you'll probably want operator==
and !=
. And maybe a way to set the flag set from either a bag o' bits (like an unsigned type or std::bitset
) or by reading a string (as in, the complement of operator<<
). Because otherwise, setting the flags using data read from a config file (for example) would be a pain.
IndexOf
can probably be a nested type of FlagSet
, unless you intend for that to be part of the public interface.
using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>;
Are you sure SFINAE is what you want to do here? It would probably make more sense to static_assert
that U
is integral.
At the same time, you might also want to static assert that U
is unsigned, because you're doing bit twiddling, which is a dodgy prospect with signed types.
And also at the same time, you could static assert that sizeof...(Fs)
is less than or equal to the number of bits in U
.
Even better, perhaps, would be to static assert that U
is an unsigned, possibly fundamental, integral type or a std::bitset
or something else with the bit-twiddling interface you need.
Most every function could also probably benefit from being noexcept
.
template<typename U_, typename...Fs_>
friend std::ostream& operator<<(std::ostream& os, FlagSet<U_,Fs_...> fs);
This doesn't really need to be a friend, because it's only using the public interface.
What I would suggest, though, is a function that writes the string form of the flag set into a buffer. Something like:
template <typename CharT>
constexpr auto write(CharT* buffer, std::size_t size, CharT zero = CharT('0'), CharT one = CharT('1')) noexcept
// write zero and one into buffer[0] through
// buffer[min(size, sizeof...(Fs)], basically as you do in to_string
With that function, you could implement both to_string()
and the insertion operator:
auto to_string(char zero = '0', char one = '1') const
auto s = std::string(sizeof...(fs), zero);
write(s.data(), s.size(), zero, one);
return s;
template <typename CharT, typename Traits, typename U, typename... Fs>
auto operator<<(std::basic_ostream<CharT, Traits>& o, FlagSet<U, Fs...> fs) ->
std::basic_ostream<CharT, Traits>&
auto buf = std::array<CharT, sizeof...(Fs)>;
write(buf.data(), buf.size());
o.write(buf.data(), buf.size());
return o;
And you'll even get support for streams of other character types, too.
The only other thing I can think off of the top of my head is that you'll probably want operator==
and !=
. And maybe a way to set the flag set from either a bag o' bits (like an unsigned type or std::bitset
) or by reading a string (as in, the complement of operator<<
). Because otherwise, setting the flags using data read from a config file (for example) would be a pain.
answered Jun 20 at 2:04
indi
3,021417
3,021417
Would be great to stress on importance of usingstatic_assert
here, as it is detrimental for useful error messages. +1 anyway, though.
â Incomputable
Jun 20 at 4:21
Thanks for your feedback, static_assert is something I will use ! For thewrite
function, why not even if I don't like the(raw pointer, size)
pattern (I consider using GSL::span ?). Usingoperator<<
would be great, as you say for serialization it would be useful.
â Julien Vernay
Jun 21 at 7:03
add a comment |Â
Would be great to stress on importance of usingstatic_assert
here, as it is detrimental for useful error messages. +1 anyway, though.
â Incomputable
Jun 20 at 4:21
Thanks for your feedback, static_assert is something I will use ! For thewrite
function, why not even if I don't like the(raw pointer, size)
pattern (I consider using GSL::span ?). Usingoperator<<
would be great, as you say for serialization it would be useful.
â Julien Vernay
Jun 21 at 7:03
Would be great to stress on importance of using
static_assert
here, as it is detrimental for useful error messages. +1 anyway, though.â Incomputable
Jun 20 at 4:21
Would be great to stress on importance of using
static_assert
here, as it is detrimental for useful error messages. +1 anyway, though.â Incomputable
Jun 20 at 4:21
Thanks for your feedback, static_assert is something I will use ! For the
write
function, why not even if I don't like the (raw pointer, size)
pattern (I consider using GSL::span ?). Using operator<<
would be great, as you say for serialization it would be useful.â Julien Vernay
Jun 21 at 7:03
Thanks for your feedback, static_assert is something I will use ! For the
write
function, why not even if I don't like the (raw pointer, size)
pattern (I consider using GSL::span ?). Using operator<<
would be great, as you say for serialization it would be useful.â Julien Vernay
Jun 21 at 7:03
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f196854%2fflagset-with-c-templates%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
You might be interested in this: gist.github.com/makulik/7963331
â ÃÂìýÃÂñ á¿¥Ã栨Â
Jun 19 at 22:09
This might also be interesting: codereview.stackexchange.com/questions/183246/â¦
â Cris Luengo
Jun 20 at 1:22