I'm writing my own typesafe enum header-only library at https://bitbucket.org/chopsii/typesafe-enums
The idea is to replace the non-type-safe c-style enum like:
enum ItemCategory
{
BLOCK,
WEAPON
};
with something that's properly type safe.
So far, my solution uses a macro that, for an example equivalent to the above enum, looks like this:
TypesafeEnum(ItemCategory,
(BLOCK)
(WEAPON)
);
And expands to something that looks like this:
template<typename InnerType>
class Wrapped {
public:
InnerType getValue() const { return _val; }
bool operator<(const Wrapped<InnerType>& rhs) const { ; return _val < rhs._val; }
bool operator>(const Wrapped<InnerType>& rhs) const { ; return _val > rhs._val; }
bool operator==(const Wrapped<InnerType>& rhs) const { ; return _val == rhs._val; }
private:
InnerType _val;
protected:
explicit Wrapped<InnerType>(const InnerType& val) : _val(val) {}
void setValue(const InnerType& val) { _val = val; }
};
class WrappedTypeItemCategory : private Wrapped<int>
{
private:
typedef const std::string* strptr;
typedef const std::string* const cstrptr;
explicit WrappedTypeItemCategory(const std::string& label, int val): Wrapped<int>(val), str(&label)
{}
cstrptr str;
public:
static WrappedTypeItemCategory make(const std::string& label, int val)
{
return WrappedTypeItemCategory(label, val);
}
void operator=(const WrappedTypeItemCategory& rhs)
{
;
setValue(rhs.getValue());
const_cast<strptr>(str) = rhs.str;
}
int getValue() const
{
return Wrapped<int>::getValue();
}
const std::string& getString() const
{
return *str;
}
bool operator<(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() < rhs.getValue();
}
bool operator>(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() > rhs.getValue();
}
bool operator==(const WrappedTypeItemCategory & rhs) const
{
;
return getValue() == rhs.getValue();
}
friend std::ostream & operator<<(std::ostream &os, const WrappedTypeItemCategory& rhs)
{
;
return os << *rhs.str << "(" << rhs.getValue() << ")";
}
};
;
namespace {
template<typename T> class ItemCategoryInner : public TypesafeEnumBase
{
public:
static const WrappedTypeItemCategory BLOCK;
static const WrappedTypeItemCategory WEAPON;
static const std::string BLOCKStr;
static const std::string WEAPONStr;
};
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::BLOCK = WrappedTypeItemCategory::make(ItemCategoryInner<T>::BLOCKStr, 0);
template<typename T> const WrappedTypeItemCategory ItemCategoryInner<T>::WEAPON = WrappedTypeItemCategory::make(ItemCategoryInner<T>::WEAPONStr, 1);
template<typename T> const std::string ItemCategoryInner<T>::BLOCKStr("ItemCategory::BLOCK");
template<typename T> const std::string ItemCategoryInner<T>::WEAPONStr("ItemCategory::WEAPON");
struct ItemCategoryTemplateConstantTrick
{};
};
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
{
private:
const WrappedTypeItemCategory* const val;
public:
class InvalidValueError : public std::runtime_error
{
public:
const int val;
InvalidValueError(int val): std::runtime_error(std::string("Invalid value given for ") + "ItemCategory::make"), val(val)
{}
};
ItemCategory(const WrappedTypeItemCategory& value): val(&value)
{}
void operator=(const ItemCategory& rhs)
{
const_cast<const WrappedTypeItemCategory*>(val) = rhs.val;
}
static ItemCategory make(const int& val)
{
if (val == ItemCategory::BLOCK.getValue()) return ItemCategory(ItemCategory::BLOCK);
if (val == ItemCategory::WEAPON.getValue()) return ItemCategory(ItemCategory::WEAPON);
;
throw InvalidValueError(val);
}
const WrappedTypeItemCategory* const getWrappedValue() const
{
return val;
}
int getValue() const
{
return val->getValue();
}
const std::string & getString() const
{
return val->getString();
}
bool operator<(const ItemCategory& rhs) const
{
return *val < *rhs.val;
}
bool operator>(const ItemCategory& rhs) const
{
return *val > *rhs.val;
}
bool operator==(const WrappedTypeItemCategory& rhs) const
{
return *val == rhs;
}
bool operator!=(const WrappedTypeItemCategory& rhs) const
{
return !(*val == rhs);
}
bool operator<=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val < rhs);
}
bool operator>=(const WrappedTypeItemCategory& rhs) const
{
return (*val == rhs || *val > rhs);
}
void print(std::ostream& os) const override
{
os << *val;
}
friend std::ostream & operator<<(std::ostream &os, const ItemCategory& rhs)
{
rhs.print(os);
return os;
}
};
;
If I manually pre-expand it, like I have done here - by pre-compiling to file - then intellisense handles it all up until the line that says:
class ItemCategory : public ItemCategoryInner<ItemCategoryTemplateConstantTrick>
At which point it starts thinking ItemCategoryInner and ItemCategoryTemplateConstantTrick are ambiguous, along with many other things on every few lines of the file.
The header that contains this code is included in many places. I know I'm violating the One Definition Rule, which is why I'm using the Template Constant Trick, but I think I need to violate the ODR as my goal is to have an easy to use Macro based typesafe replacement for C++ enums.
I'm not sure if it's the violation of ODR that is the cause of my issues, or something else. I tried __declspec(selectany) but it didn't seem to help - and I would prefer if this macro would be eventually cross-platform, because if it works out, I have other projects I would use it in.
Either way, the .cpp files etc that make use of the enum are able to, and intellisense correctly suggests the options.
However, on a possibly related note, if I don't pre-expand the macro, intellisense isn't able to parse it and it doesn't know what a ItemCategory is at all, even though it compiles and works fine.
I just want my intellisense to work properly with my typesafe enums - it slows down intellisense and confuses it in other code in the same project.
Related
I have this piece of code:
class ISerializable
{
public:
virtual bool operator==(const ISerializable* /*value*/) const { return false;};
virtual bool operator!=(const ISerializable* /*value*/) const { return true;};
};
class Point2I : public ISerializable
{
public:
bool operator==(const Point2I& value)
{
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value)
{
return !(*this == value);
}
public:
int x;
int y;
};
class Coordinate : public ISerializable
{
public:
virtual bool operator==(const Coordinate& value) const;
virtual bool operator!=(const Coordinate& value) const;
};
It is causing me -Woverloaded-virtual warning on gcc compiler.
I understand this warning due to that function declaration in Point2I hides virtual functions from ISerializable.
But I am not sure if just missing const in Point2I can cause this warning.
Can you please help me understand if it is const which is causing this warning or something else? Warning description from gcc didn't mention anything specifically.
Update:
I found another class Coordinate in my code base which was already overriding this and gcc not throwing warning for this. Only difference in Point2I and Coordinate is I didn't declare it virtual with const in Point2I. It appears just const is hiding base class declaration.
if it is const which is causing this warning or something else?
I'd say that it's something else, namely that you are not actually overriding the base class methods, even if you add const.
The argument const ISerializable* is not the same as const Point2I&.
One solution could be to override the base class methods, using const ISerializable& as the argument, and cast in the overridden methods:
class ISerializable {
public:
// add virtual destructor if you need to delete objects through
// base class pointers later:
virtual ~ISerializable() = default;
virtual bool operator==(const ISerializable&) const { return false; }
virtual bool operator!=(const ISerializable&) const { return true; }
};
class Point2I : public ISerializable {
public:
bool operator==(const ISerializable& value) const override {
auto rhs = dynamic_cast<const Point2I*>(&value);
// rhs will be nullptr if the cast above fails
return rhs && (x == rhs->x && y == rhs->y);
}
bool operator!=(const ISerializable& value) const override {
return !(*this == value);
}
private:
int x = 0;
int y = 0;
};
Example usage:
#include <iostream>
class Foo : public ISerializable { // another ISerializable
public:
};
int main() {
Point2I a, b;
std::cout << (a == b) << '\n'; // true - using Point2I::operator==
Foo f;
std::cout << (a == f) << '\n'; // false - using Point2I::operator==
std::cout << (f == a) << '\n'; // false - using ISerializable::operator==
// this makes the default implementation in ISerializable utterly confusing:
std::cout << (f == f) << '\n'; // false - using ISerializable::operator==
}
Another possible solution could be using CRTP but this would not work if you want to compare different types derived from ISerializable<T>:
template<class T>
class ISerializable {
public:
virtual ~ISerializable() = default;
virtual bool operator==(const T&) const = 0;
virtual bool operator!=(const T&) const = 0;
};
class Point2I : public ISerializable<Point2I> {
public:
bool operator==(const Point2I& value) const override {
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value) const override {
return !(*this == value);
}
public:
int x;
int y;
};
There are two problems.
The first one is different types of parameters
In these functions the parameters have the pointer type const ISerializable*
virtual bool operator==(const ISerializable* /*value*/) const { return false;};
virtual bool operator!=(const ISerializable* /*value*/) const { return true;};
and in these functions the parameters have the referenced type const Point2I&
bool operator==(const Point2I& value)
{
return (x == value.x && y == value.y);
}
bool operator!=(const Point2I& value)
{
return !(*this == value);
}
The second one is that the first functions are constant member functions while the second functions are not constant member functions.
I have trying to use a class as a key in an std::map. I have read documentations and I know I have to type some sort of sorting rule because my std::map is a binary search tree. The problem arise because the class key have another classes in it. Can someone get me some advice how to build the operators?
MasterRenderer file
std::map<TexturedModel, std::vector<Entity>> entites;
void MasterRenderer::processEntity(Entity entity)
{
TexturedModel model = entity.getModel();
auto search = entites.find(model);
if (search != entites.end()) {
//found
entites[model].emplace_back(entity);
}
else {
//not found
entites[model].emplace_back(entity);
}
std::cout << entites[model].size() << std::endl;
}
TexturedModel.h
TexturedModel(RawModel model, ModelTextures
texture)
:m_model(model), m_texture(texture) {
};
friend bool operator<(const TexturedModel& m,
const
TexturedModel& m2) {
return m.m_model < m2.m_model || m.m_model ==
m2.m_model && m.m_texture < m2.m_texture;
}
private:
RawModel m_model;
ModelTextures m_texture;
};
Rawmodel.h
unsigned int VaoID;
unsigned int Vertecies;
RawModel(unsigned int vaoID, unsigned int
vertecies)
:VaoID(vaoID), Vertecies(vertecies) {};
friend bool operator <(const RawModel& rhs, const
RawModel& rhs2)
{
return rhs.get() < rhs2.get();
}
friend bool operator ==(const RawModel& rhs, const
RawModel& rhs2)
{
return rhs.get() == rhs2.get();
}
const RawModel* get() const {
return this;
}
ModelTextures.h
ModelTextures(unsigned int ID)
:textureID(ID) {};
friend bool operator<(const ModelTextures& rhs,
const ModelTextures& rhs2)
{
return rhs.get() < rhs2.get();
}
const ModelTextures* get() const{
return this;
}
private:
unsigned int textureID;
float shineDamper = 1.0f;
float reflectivity = 0.0f;
};
friend bool operator<(const ModelTextures& rhs,
const ModelTextures& rhs2)
{
return rhs.get() < rhs2.get();
}
const ModelTextures* get() const{
return this;
}
this orders by address of the object, not content. That violates the requirements of std::map.
friend auto as_tie(const ModelTexture& m) {
return std::tie(m.textureID, m.shineDamper, m.reflexivity);
}
friend bool operator<(const ModelTextures& rhs,
const ModelTextures& rhs2)
{
return as_tie(rhs) < as_tie(lhs);
}
repeat this pattern for TexturedModel and RawModel.
If you are stuck in c++11 you have to manually write the return type of as_tie or use decltype.
friend auto as_tie(const ModelTexture& m)
-> decltype(std::tie(m.textureID, m.shineDamper, m.reflexivity))
{
return std::tie(m.textureID, m.shineDamper, m.reflexivity);
}
I want to create efficient and easy to use value type.
Base of the Value is a boost::variant (and std::variant in the future), but I'm new in it.
And I have some questions:
In the code below, is it necessary to use recursive variant?
Is it possible to not inherit from the boost::variant? Maybe more efficient way exists?
Do you have any comments or suggestions on the code below (it's not fully completed code, but only a draft)?
class Value;
typedef std::string String;
typedef std::vector<char> BinData;
typedef String URL;
typedef unsigned long long UID;
TSW_STRONG_TYPEDEF(std::time_t, Time)
typedef std::vector<Value> ValueArray;
typedef std::vector<String> StringArray;
//typedef std::pair<String, Value> NameValue;
typedef std::list<Value> ValueList;
typedef std::list<String> StringList;
typedef std::map<String, String> StringStringMap;
typedef std::map<String, Value> NameValueMap;
struct monostate
{
monostate() = default;
};
constexpr bool operator<(monostate, monostate) noexcept { return false; }
constexpr bool operator>(monostate, monostate) noexcept { return false; }
constexpr bool operator<=(monostate, monostate) noexcept { return true; }
constexpr bool operator>=(monostate, monostate) noexcept { return true; }
constexpr bool operator==(monostate, monostate) noexcept { return true; }
constexpr bool operator!=(monostate, monostate) noexcept { return false; }
typedef monostate Null;
class Object
{
public:
Object() = delete;
Object(const Object &other) = default;
Object(Object &&other);
Object(const String &name);
Object(String &&name);
Object(const String &name, const NameValueMap &fields);
Object(String &&name, const NameValueMap &fields);
Object(const String &name, NameValueMap &&fields);
Object(String &&name, NameValueMap &&fields);
Object &operator=(const Object &other) = default;
Object &operator=(Object &&other);
public:
const String &get_name() const;
const NameValueMap &get_fields() const;
public:
bool operator<(const Object &other) const noexcept;
bool operator>(const Object &other) const noexcept;
bool operator<=(const Object &other) const noexcept;
bool operator>=(const Object &other) const noexcept;
bool operator==(const Object &other) const noexcept;
bool operator!=(const Object &other) const noexcept;
private:
String name_;
NameValueMap fields_;
};
enum class ValueType
{
Undefined, Null, Array, BinData, Boolean, DoubleNumber, Int64Number, String, Time, Object
};
// Types ordnung need to be same with ValueType ordnung.
/// Base for the Value class
typedef boost::variant<monostate, Null, ValueArray, BinData, bool, double, int64_t, String, Time, Object> ValueBase;
/**
* #brief The Value class, implements common framework value.
*
* This class is a container, which can store multiple values, including Values containers.
*
* #note
* Class based on a variant class. It may be either boost::variant or std::variant in C++17 and higher.
*/
class Value : public ValueBase
{
public:
using ValueBase::ValueBase;
Value() = default;
Value(const String::value_type *v) : ValueBase(String(v)) {}
public:
bool is_array() const { return static_cast<ValueType>(which()) == ValueType::Array; }
bool is_bool() const { return static_cast<ValueType>(which()) == ValueType::Boolean; }
bool is_bindata() const { return static_cast<ValueType>(which()) == ValueType::BinData; }
bool is_double() const { return static_cast<ValueType>(which()) == ValueType::DoubleNumber; }
bool is_int64() const { return static_cast<ValueType>(which()) == ValueType::Int64Number; }
bool is_null() const { return static_cast<ValueType>(which()) == ValueType::Null; }
bool is_object() const { return static_cast<ValueType>(which()) == ValueType::Object; }
bool is_string() const { return static_cast<ValueType>(which()) == ValueType::String; }
bool is_time() const { return static_cast<ValueType>(which()) == ValueType::Time; }
bool is_undefined() const { return static_cast<ValueType>(which()) == ValueType::Undefined; }
public:
bool as_bool() const { return as<bool>(); }
BinData &as_bindata() { return as<BinData>(); }
double as_double() const { return as<double>(); }
int64_t as_int64() const { return as<int64_t>(); }
Object &as_object() { return as<Object>(); }
String &as_string() { return as<String>(); }
Time &as_time() { return as<Time>(); }
ValueArray &as_array() { return as<ValueArray>(); }
public:
ValueType value_type() const { return static_cast<ValueType>(which()); }
public:
template <typename T>
const T& as() const { return boost::get<T>(*this); }
template <typename T>
T& as() { return boost::get<T>(*this); }
template <typename T>
const T& as(const T& default_value) const { return type() == typeid(T) ? boost::get<T>(*this) : default_value; }
template <typename T>
T& as(const T& default_value) { return type() == typeid(T) ? boost::get<T>(*this) : default_value; }
template <typename T> boost::optional<T> as_optional() { return boost::make_optional(type() == typeid(T), as<T>()); }
public:
bool operator==(const ValueBase &other) const { return ValueBase::operator==(other); }
bool operator<(const ValueBase &other) const { return ValueBase::operator<(other); }
bool operator>(const ValueBase &other) const { return !((*this) < other || (*this) == other); }
bool operator<=(const ValueBase &other) const { return ((*this) < other || (*this) == other); }
bool operator>=(const ValueBase &other) const { return !((*this) < other); }
bool operator!=(const ValueBase &other) const { return !((*this) == other); }
private:
// Force compile error, prevent Variant(bool) to be called
Value(void *) = delete;
};
Looks okay to me.
You can do without recursive variants IFF your standard library implementation allows instantiation of the container classes for incomplete types.
I'd note that since everything is tied to the base-class publicly, there is nothing about the implementation that can change without also changing the (binary) interface. Therefore I'd surely implement all the members inline so that the compiler will optimize them even without LTO.
It is not clear to me what to_X members do (possibly just a<X> but possibly something else depending on can_convert()?). If it's just a wrap around as_<> I'd rename them as_X() etc.
You might also want to add optional<>-like members like
template <typename T> T const& get_value_or(T const& default_value) const;
And possibly
template <typename T> optional<T> get() const;
// with boost optional you can prevent a copy²:
template <typename T> optional<T const&> get() const;
This enables code like:
if (auto& s = value.get<String>()) {
std::cout << "The string value is '" << *s << "'\n";
} else {
std::cout << "Value has no string value\n";
}
¹ this is not - yet - standard specified. You can always use Boost Container instead which promises this, as well as non-allocating construction
² just make sure you do not allow the operation on a rvalue, to remove a predictable class of errors, so e.g.
template <typename T> optional<T const&> get()&& = delete;
I am trying to use std::find with two different types but providing the necessary boolean operators.
class FooDetails
{
public:
FooDetails(int size = 5)
: m_size(size) { /* empty */ }
bool operator<(const FooDetails& other) const { return m_size < other.m_size; }
bool operator==(const FooDetails& other) const { return m_size == other.m_size; }
private:
int m_size;
};
class Foo
{
public:
Foo(int size)
: m_details(size) { /* empty */}
bool operator==(const Foo& other) const { return m_details == other.m_details; }
bool operator==(const FooDetails& other) const {return m_details == other; }
bool operator<(const Foo& other) const { return m_details < other.m_details; }
bool operator<(const FooDetails& other) const { return m_details < other; }
FooDetails m_details;
};
bool operator==(const FooDetails& lhs, const Foo& rhs) { return lhs == rhs.m_details; }
bool operator==(const Foo& lhs, const FooDetails& rhs) {return lhs.m_details == rhs; }
bool operator<(const FooDetails& lhs, const Foo& rhs) { return lhs < rhs.m_details; }
bool operator<(const Foo& lhs, const FooDetails& rhs) { return lhs.m_details < rhs; }
int main() {
std::vector<Foo> haystack = { FooDetails(5), FooDetails(6), FooDetails(7) };
FooDetails needle(6);
std::find(haystack.begin(), haystack.end(), needle);
return 0;
}
Since std::find uses operator== I would expect this to work, since all necessary functions are provided. However this does not compile. Why is that and how do I fix it?
I know I could use std::find_if, but I assume that's a bit slower and even if it's not I'd like to know why std::find doesn't work.
I've changed your code to this:
#include <algorithm>
class FooDetails
{
public:
FooDetails(int size = 5)
: m_size(size) { /* empty */ }
bool operator<(const FooDetails& other) const { return m_size < other.m_size; }
bool operator==(const FooDetails& other) const { return m_size == other.m_size; }
private:
int m_size;
};
class Foo
{
public:
Foo(int size)
: m_details(size) { /* empty */}
FooDetails m_details;
};
bool operator==(const FooDetails& lhs, const Foo& rhs) { return lhs == rhs.m_details; }
bool operator==(const Foo& lhs, const FooDetails& rhs) {return lhs.m_details == rhs; }
bool operator<(const FooDetails& lhs, const Foo& rhs) { return lhs < rhs.m_details; }
bool operator<(const Foo& lhs, const FooDetails& rhs) { return lhs.m_details < rhs; }
int main() {
std::vector<Foo> haystack = { Foo(5), Foo(6), Foo(7) };
FooDetails needle(6);
std::find(haystack.begin(), haystack.end(), needle);
return 0;
}
And it successfully compiles. Your original version contains two errors:
You try to create std::vector<Foo> initialising it with std::initialization_list<FooDetails>.
You have provided too many comparison operators: both free versions and members of the Foo struct. So during lookup compiler complains that it doesn't know which one of them to choose. You should leave only one of them.
You have no such constructor in Foo class which constructs it from FooDetails. You need to define the following and your code will work:
Foo(const FooDetails& d) : m_details(d) {}
Say I have a Storage class:
class Storage
{
public:
const string& get() const { return m_data; }
const char& get(int ind) const { return m_data[ind]; }
const string& get(int s_ind, int e_ind) const { /* TBD */ }
private:
string m_data; ///< Data is so big that part of it is stored on disk
}
Say I have a Writer class that gets const Storage& and needs to access its data.
My question, is there a way to implement:
const string& get(int s_ind, int e_ind) const;
i.e, get const access to only a part of a string.
Notes:
get() is called countless of times and it is the bottleneck of my application. I'd like to avoid allocating new objects when accessing data.
is there a way to implement:
const string& get(int s_ind, int e_ind) const;
i.e, get const access to only a part of a string.
Definitely not.
What is often done - and may resolve your bottleneck - is to create a class that stores a const char* and size_t (or equally begin and end const char*s, or iterators but there's no reason to limit this to use for data in std::strings).
You could then create an object that "references" text inside a string, and use it until any of the events that would invalidate an iterator or reference to those characters happens - see the Standard or e.g. cppreference. It's possible to support stream output, comparisons, indexing etc. driven off the std::string hosted data.
Clearly you won't be able to pass such a class to functions that hardcode std::string type, but you could write it to have a similar interface, which should lessen pain.
Just as a taster (hasn't seen a compiler / flesh out as needed)...
class Text_Ref
{
public:
Text_Ref(const char* p, size_t n) : p_(p), n_(n) { }
// intuitive values for &text_ref[x] BUT text_ref[n] may not be nul
const char& operator[](size_t o) const { return p_[n]; }
*** OR ***
// text_ref[n] is nul BUT can't use &text_ref[x]
char operator[](size_t o) const { return o == n ? '\0' : p_[n]; }
// same design trade off as the operator[] alternatives above
char at(size_t o) const
{
if (o > n) throw std::out_of_range();
return o == n ? '\0' : p_[n];
}
bool empty() const { return n == 0; }
size_t size() const { return n; }
size_t length() const { return n; }
int compare(const char* p) const
{
do
{
if (*p != *p_)
return (int)*p_ - *p;
} while (*p);
return 0;
}
bool operator< (const char* p) const { return compare(p) < 0; }
bool operator<=(const char* p) const { return compare(p) <= 0; }
bool operator==(const char* p) const { return compare(p) == 0; }
bool operator!=(const char* p) const { return compare(p) != 0; }
bool operator>=(const char* p) const { return compare(p) >= 0; }
bool operator> (const char* p) const { return compare(p) > 0; }
private:
const char* p_;
size_t n;
};
inline std::ostream& operator<<(std::ostream& os, const Text_Ref& t)
{
return os.write(t.data(), t.size());
}