I'm trying to create a struct of arrays:
auto x = MakeMegaContainer<StructA, StructB, StructC>();
Which I want to, at compile time, produce a structure like:
struct MegaContainer {
std::tuple< Container<StructA>, Container<StructB>, Container<StructC> > Data;
};
The creation method is non-negotiable, and I think tuples are the best way to go, as later I could access one container's struct elements with std::get<StructA>(x)[index], if my container allows [].
I tried doing something like:
template<typename... LilStructs>
class StructStorage {
public:
template<typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Get(id); // Get is defined for MyContainer
}
template<typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Contains(id); // Contains is defined for MyContainer
}
private:
std::tuple<MyContainer<LilStructs>...> m_Storage;
};
But I don't think this works. I'm not sure how to take the variadic arguments and "wrap" them with a container
What should I do instead?
Follow up question: The MyContainer also takes additional arguments that customise it, like a max size. Is there a nice way of passing that, something like template<typename... LilStructs, uint64_t MAX_SIZE=4096>?
Here's a stripped down version of the container for a minimal reproducible example:
template<typename T_elem, typename T_int = uint64_t, T_int MAX_SIZE = 4096>
class MyContainer{
public:
MyContainer() = default;
~MyContainer() = default;
bool Contains(const T_int& id) const;
T_elem& operator[](const T_int& id);
T_elem& Get(const T_int& id);
private:
std::map<T_int, T_elem> m_Map;
T_int m_Size = 0;
};
template<typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(
const T_int& id
) const {
return m_Map[id] < m_Size;
}
template<typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::operator[](const T_int& id) {
return m_Map[id];
}
template<typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::Get(const T_int& id) {
return operator[](id);
}
When I try to compile:
void Test() {
struct SA { int x; };
struct SB { float x; };
struct SC { char x; };
auto& tests = StructStorage <SA, SB, SC>();
bool x = !tests.ContainsStruct<SA>(5);
}
I get the error:
c:\...\test.h(18): error C2039: 'Contains': is not a member of 'Trial::SA'
As I said, I know the error is in the std::tuple<MyContainer<LilStructs>...> m_Storage; line, and the error is indpendent of the implementation of MyContainer (provided that Contains and Get are implemented), but I do not know what to replace it with, or how to achieve the functionality I'm looking for (which was already described).
Look at this part:
template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Get(id); // Get is defined for MyContainer
}
template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Contains(id); // Contains is defined for MyContainer
}
The m_Storage tuple is a tuple of MyContainer<LilStructs>...s and not LilStructs...s so what you actually want to do is this:
template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Get(id); // Get is defined for MyContainer
// ^^^^^^^^^^^^^^^^^^^^^^
}
template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Contains(id); // Contains is defined for MyContainer
// ^^^^^^^^^^^^^^^^^^^^^^
}
Also, your Contains() function is wrong. Use this:
template <typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(const T_int& id) const {
return m_Map.find(id) != m_Map.end();
}
Full working code:
#include <cstdint>
#include <cassert>
#include <tuple>
#include <map>
template <typename T_elem, typename T_int = uint64_t, T_int MAX_SIZE = 4096>
class MyContainer{
public:
MyContainer() = default;
~MyContainer() = default;
bool Contains(const T_int& id) const;
T_elem& operator[](const T_int& id);
T_elem& Get(const T_int& id);
private:
std::map<T_int, T_elem> m_Map;
T_int m_Size = 0;
};
template <typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(const T_int& id) const {
return m_Map.find(id) != m_Map.end();
}
template <typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::operator[](const T_int& id) {
return m_Map[id];
}
template <typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::Get(const T_int& id) {
return operator[](id);
}
template <typename ...LilStructs>
class StructStorage {
public:
template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Get(id);
}
template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Contains(id);
}
private:
std::tuple<MyContainer<LilStructs>...> m_Storage;
};
int main() {
struct SA { int x; };
struct SB { float x; };
struct SC { char x; };
auto tests = StructStorage<SA, SB, SC>();
assert(!tests.ContainsStruct<SA>(5));
}
Demo
Related
I have a class that wraps some type and attaches a Dimension. It should be convertible to the underlying type, but only if Dim=0. The conversion operator should not be callable in other cases (so a static_assert in the function would not work for me).
The following code works, if the enable_if-construction is deleted, but not in this form.
template<class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template<int D = Dim, typename = typename std::enable_if<D == 0>::type>
operator T() { return _value; }
private:
T _value;
};
auto main() -> int
{
auto a = double{0};
auto u = Unit<double, 0>{a};
auto i = static_cast<int>(u);
return i;
}
What is the reason for this and is there a work around to allow the cast, but also restrict the conversion?
As I understand, you want:
template <class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template <typename U, int D = Dim,
std::enable_if_t<D == 0 && std::is_convertible_v<T, U>, int> = 0>
operator U() { return _value; }
private:
T _value;
};
Demo
And in C++20, look nicer
template<class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template <typename U>
requires(Dim == 0 && std::is_convertible_v<T, U>)
operator U() const { return _value; }
private:
T _value;
};
Demo
I'm trying to write a template class that will wrap a type that could be either a primitive (uint8_t up to float) or a char* string. If the class wraps a primitive type, setting its value to any primitive will do a straight cast, while setting a char* will to an ascii to primitive conversion (atoi, sprintf, etc). If it wraps a char*, the class should manage an internal char* buffer and convert primitive to char*.
If I limit my example to only uint32_t values, this is the template that would deal with any primitive type (with obvious error checking details omitted):
template<typename T>
class Measurement
{
public:
Measurement();
~Measurement();
void setU32Value(uint32_t value);
void setStringValue(const char* std);
uint32_t asU32() const;
const char* asString() const;
private:
T _value;
};
template<typename T>
void Measurement<T>::setU32Value(uint32_t value)
{
_value = (T)value;
}
template<typename T>
void Measurement<T>::setStringValue(const char* value)
{
_value = (T)::atoi(value);
}
template<typename T>
uint32_t Measurement<T>::asU32() const
{
return (uint32_t)_value;
}
template<typename T>
const char* Measurement<T>::asString() const
{
ostringstream oss;
oss << _value;
return oss.str();
}
But it won't work if I try to Measurement<char*> because it won't convert setU32Value wont' convert "value" to a string representation and the asU32() won't convert it back.
Can anyone suggest a way to make this work?
Thanks.
Your class definitely should not manually manage the lifetime of its string. Please see the Single-responsibility principle, The rule of three/five/zero and RAII. So the string storage type should be std::string and not char*.
C++20 concepts:
#include <string>
#include <concepts>
#include <type_traits>
template<typename T>
class Measurement
{
private:
T _value{};
public:
Measurement() = default;
template <std::integral U> requires std::integral<T>
void setIntValue(U value)
{
_value = static_cast<U>(value);
}
template <std::integral U> requires std::same_as<T, std::string>
void setIntValue(U value)
{
_value = std::to_string(value);
}
void setStringValue(const std::string& str) requires std::integral<T>
{
_value = static_cast<T>(std::stoll(str));
}
void setStringValue(std::string str) requires std::same_as<T, std::string>
{
_value = std::move(str);
}
template <std::integral U> requires std::integral<T>
U asInt() const
{
return static_cast<U>(_value);
}
template <std::integral U> requires std::same_as<T, std::string>
U asInt() const
{
return static_cast<U>(std::stoll(_value));
}
std::string asString() const requires std::integral<T>
{
return std::to_string(_value);
}
std::string asString() const requires std::same_as<T, std::string>
{
return _value;
}
};
Usage example:
auto test()
{
{
auto m = Measurement<long>{};
m.setIntValue<int>(24);
int a = m.asInt<int>();
std::string s = m.asString();
m.setStringValue("11");
a = m.asInt<int>();
s = m.asString();
}
{
auto m = Measurement<std::string>{};
m.setIntValue<int>(24);
int a = m.asInt<int>();
std::string s = m.asString();
m.setStringValue("11");
a = m.asInt<int>();
s = m.asString();
}
}
Template specialization
#include <string>
#include <type_traits>
template <class T, class Enable = void> class Measurement;
template <class T>
class Measurement<T, std::enable_if_t<std::is_integral_v<T>>>
{
private:
T _value{};
public:
Measurement() = default;
template <std::integral U>
void setIntValue(U value)
{
_value = static_cast<U>(value);
}
void setStringValue(const std::string& str)
{
_value = static_cast<T>(std::stoll(str));
}
template <std::integral U>
U asInt() const
{
return static_cast<U>(_value);
}
std::string asString() const requires std::integral<T>
{
return std::to_string(_value);
}
};
template <>
class Measurement<std::string>
{
private:
std::string _value{};
public:
Measurement() = default;
template <std::integral U>
void setIntValue(U value)
{
_value = std::to_string(value);
}
void setStringValue(std::string str)
{
_value = std::move(str);
}
template <std::integral U>
U asInt() const
{
return static_cast<U>(std::stoll(_value));
}
std::string asString() const
{
return _value;
}
};
Same usage as before.
I have to say that the class seems too bloated. I would instead have a class for storing the measurement and a separate utility to help convert between measurement and integers/string.
How would generic accessors for a property class and their defaults be implemented?
I roughly think it would look as follows:
template<typename Type,
typename Getter = /* How to implement a default setter? */
typename Setter =
class Property {
Getter get; /* Is this right? How is it called then? */
Setter set;
Property(Type value, Getter getter, Setter setter) ...
};
Getter and Setter should be able to be given as lambdas. Is this the right approach, and how do I continue?
You could agree to some kind of structural interface for getters and setters, and then implement something like this:
template <typename T> struct default_getter
{
T & operator()(T & x) const { return x; }
T const & operator()(T const & x) const { return x; }
};
template <typename T> struct default_setter
{
template <typename U>
void operator()(T & x, U && u) const { x = std::forward<U>(u); }
};
template <typename T,
typename Getter = default_getter<T>,
typename Setter = default_setter<T>>
class Property
{
struct PropertyImpl : Getter, Setter
{
T value;
};
PropertyImpl impl;
public:
template <typename U>
void set(U && u)
{
static_cast<Setter &>(impl)(impl.value, std::forward<U>(u));
}
T & get()
{
static_cast<Getter &>(impl)(impl.value);
}
T const & get() const
{
static_cast<Getter const &>(impl)(impl.value);
}
};
Now you can use it like so:
struct Foo
{
Property<Bar> bar;
};
Foo x;
x.bar.get();
x.bar.set(10);
I would propose a solution using std::function.
template<typename T>
struct Property
{
typedef std::function<T()> GetterFunc;
typedef std::function<void(const T&)> SetterFunc;
Property (T value, GetterFunc getter, SetterFunc setter)
: m_getter(getter)
, m_setter(setter)
{
}
Property(T value)
: m_getter( [value](){ return value; }
, m_setter ( [](const T&) { } ) // I have know clue what behaviour you want here
{
}
T Get() { return m_getter(); }
void Set(const T& value) { m_setter(value); }
private:
GetterFunc m_getter;
SetterFunc m_setter;
}
Is there an implementation of a "for each" macro like BOOST_FOREACH that doesn't need Boost?
I've made one.
It should work with C++11 r-value references in GCC and MSVC, but I haven't tested it well so if there's any errors please let me know.
Edit: I added support for strings.
#include <iterator>
#include <utility>
#define foreach(v, r) \
if (fe_detail::any const &fe_r_ = \
fe_detail::make_range(r)) { } else \
if (fe_detail::any const &fe_b_ = \
fe_detail::begin(fe_r_, ((void)0, 1) ? NULL : fe_detail::twrap(r))) { } else \
if (fe_detail::equal( \
fe_b_, \
fe_detail::end(fe_r_, ((void)0, 1) ? NULL : fe_detail::twrap(r)), \
((void)0, 1) ? NULL : fe_detail::twrap(r))) { } else \
for (bool fe_c_ = false; \
!fe_c_ && !fe_detail::equal( \
fe_b_, \
fe_detail::end(fe_r_, ((void)0, 1) ? NULL : fe_detail::twrap(r)), \
((void)0, 1) ? NULL : fe_detail::twrap(r)); \
fe_detail::advance(fe_b_, 1, ((void)0, 1) ? NULL : fe_detail::twrap(r))) \
for (v = (fe_detail::move(*fe_detail::iter(fe_b_, ((void)0, 1) ? NULL : fe_detail::twrap(r)))); \
fe_c_ = !fe_c_; )
namespace fe_detail
{
// Container traits
template<class C, class It> struct CT
{
typedef It It;
static It begin(C &c) { return c.begin(); }
static It end(C &c) { return c.end(); }
};
// Range traits
template<class R> struct RT : public CT<R, typename R::iterator> { };
template<class R> struct RT<R const> : public CT<R const, typename R::const_iterator> { };
template<class R> struct RT<R &> : public RT<R> { };
template<class T, bool B = T::value> struct enable_if;
template<class T> struct enable_if<T, true> { typedef T type; };
template<class T> struct is_char { static bool const value = false; };
template<class T> struct is_char<T const> : public is_char<T> { };
template<class T> struct is_char<T volatile> : public is_char<T> { };
template<> struct is_char<char> { static bool const value = true; };
template<> struct is_char<wchar_t> { static bool const value = true; };
template<class Ch> struct RT<Ch *> : enable_if<is_char<Ch> >
{
typedef Ch *It;
static It begin(It a) { return &a[0]; }
static It end(It a) { return &a[std::char_traits<Ch>::length(a)]; }
};
template<class R, size_t N> struct RT<R[N]>
{
typedef R *It;
static It begin(It a) { return &a[0]; }
static It end(It a) { return &a[N - (((void)0, is_char<R>::value) ? 1 : 0)]; }
};
template<class It> struct RT<std::pair<It, It> >
{
typedef It It;
static It begin(std::pair<It, It> const a) { return a.first; }
static It end(std::pair<It, It> const a) { return a.second; }
};
struct any { operator bool() const { return false; } };
template<class T> struct type_wrap { type_wrap(bool = false) { } };
template<class T> class wrap : public any
{ wrap &operator =(wrap const &); public: mutable T v; wrap(T v) : any(), v(v) { } };
template<class T> class wrap<T const> : public any
{ wrap &operator =(wrap const &); public: T const v; wrap(T const v) : any(), v(v) { } };
template<class T, size_t N> class wrap<T[N]> : public any
{ wrap &operator =(wrap const &); public: T (&v)[N]; wrap(T (&v)[N]) : any(), v(v) { } };
template<class T> class wrap<T const &> : public wrap<T const>
{ wrap &operator =(wrap const &); public: wrap(T const &v) : wrap<T const>(v) { } };
template<class T, size_t N> wrap<T[N]> make_range(T (&r)[N]) { return r; }
template<class T, size_t N> type_wrap<T[N]> twrap(T (&r)[N]) { throw 0; }
template<class It> type_wrap<std::pair<It, It> > twrap(std::pair<It, It> const &p) { throw 0; }
#if defined(_MSC_VER) && _MSC_VER >= 1600 || defined(__RVALUE_REFERENCE) || defined(__GXX_EXPERIMENTAL_CXX0X__)
template<class R> struct RT<R &&> : public RT<R> { };
template<class T> class wrap<T &&> : public wrap<T> { public: wrap(T &&v) : wrap<T>(std::move(v)) { } };
template<class R> wrap<R &&> make_range(R &&r) { return wrap<R &&>(std::forward<R>(r)); }
template<class R> type_wrap<R> twrap(R &&) { throw 0; }
using std::move;
#else
template<class R> wrap<R> make_range(R &r) { return r; }
template<class R> wrap<R const &> make_range(R const &r) { return r; }
template<class R> type_wrap<R> twrap(R &) { throw 0; }
template<class R> type_wrap<R const &> twrap(R const &) { throw 0; }
template<class T> T &move(T &v) { return v; }
template<class T> T const &move(T const &v) { return v; }
#endif
template<class R> wrap<typename RT<R>::It> begin(any const &r, type_wrap<R>)
{ return RT<R>::begin(static_cast<wrap<R> const &>(r).v); }
template<class R> wrap<typename RT<R>::It> end(any const &r, type_wrap<R>)
{ return RT<R>::end (static_cast<wrap<R> const &>(r).v); }
template<class R> bool equal(any const &i, any const &j, type_wrap<R>)
{ return static_cast<wrap<typename RT<R>::It> const &>(i).v == static_cast<wrap<typename RT<R>::It> const &>(j).v; }
template<class R> void advance(any const &i, typename std::iterator_traits<typename RT<R>::It>::difference_type const d, type_wrap<R>)
{ return std::advance(static_cast<wrap<typename RT<R>::It> const &>(i).v, d); }
template<class R> typename RT<R>::It &iter(any const &i, type_wrap<R>)
{ return static_cast<wrap<typename RT<R>::It> const &>(i).v; }
}
You can use it like:
#include <vector>
#include <iostream>
std::vector<int> make_vect()
{
std::vector<int> v;
v.push_back(5);
v.push_back(8);
v.push_back(10);
return v;
}
int main()
{
foreach (int c, make_vect())
{
std::cout << c << std::endl;
}
}
I would like to overload the [] operator for a template class in respect with the template parameters. Like so:
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
I would use this class like so:
int main()
{
a_map<float, prop, key_a, key_b> pm;
}
Basically I want to be able to access the elements inside the _values vector without having to worry to much about the Key types. All that matters is that they have an index() member.
However I get the following error
error C2535: 'const Property &a_map::operator
[](const Key1 &) const' : member function already defined or
declared
even though key_a and key_b are two totaly different types class templates.
Am I missing something? Is the compiler afraid that in certain situation Key1<T> and Key2<T> might actually be the same type?
Edit
These are the class templates used in main
template<typename T>
struct prop
{
T weight;
T height;
};
template<typename T>
class key_a
{
public:
int index() { return _i; }
private:
int _i;
};
template<typename T>
class key_b
{
public:
int index() { return 3; } // Always return 3
Edit
I'm using MVC++ 2008 compiler.
Since both of your operator[] are the same except for the argument type, why not template them?
template <typename TT>
const Property<T>& operator[](const TT& k) const
{
return _values[k.index()];
}
You need to declare your index() functions as const because inside the a_map template you are invoking them through const objects
template<typename T> class key_a {
public:
int index() const // <- `const` is necessary
{ return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
int index() const // <- `const` is necessary
{ return 3; }
};
But otherwise, everything compiles and work fine for me.
Tried it in VS2010 compiler and get the same error as yours. This is obviously a compiler bug in MSVC++ compilers. Handling of template-template arguments is implemented incorrectly.
To work around the problem I was able to use this technique
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const typename Key1<T>::self& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const typename Key2<T>::self& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
and define the key templates as
template<typename T> class key_a {
public:
typedef key_a self;
int index() const { return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
typedef key_b self;
int index() const { return 3; }
};
This is inelegant, but it makes it work correctly with MSVC++ compilers.
Compiles fine like this... note how Prop/K1/K2 should be defined.
#include <vector>
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
template <typename T> struct K1 { int index() const { return 0; } };
template <typename T> struct K2 { int index() const { return 0; } };
template <typename T> struct Prop { };
int main()
{
a_map<float, Prop, K1, K2> m;
}