I should start by saying that my knowledge of C++ is pretty limited. I have some understanding of templates and specialization, but I'm by no means an experienced C++ programmer. For example I've today learnt about "aliases" which are not quite the same as "typedefs" and this is completely news to me.
I've been reading up a bit on alias template functions, but I have to admit that I find most examples very cryptic, so I've come up with a very simple use case, see below.
#include <iostream>
// A fictitious 24-bit type, that can be manipulated like any other type but can be distinguished from the underlying 32-bit type
using int24_t = int32_t;
template<typename T>
void foo(T x)
{
std::cout << "x = " << x << std::endl;
}
template<>
void foo<int24_t>(int24_t x)
{
std::cout << "24-bit specialization - x = " << x << std::endl;
}
int main(void)
{
foo<int16_t>(0);
foo<int24_t>(1);
foo<int32_t>(2); // Indistinguishable from 24-bit
}
Is it possible to do what I want, i.e. have a specialization of foo<int24_t> but also have a general purpose implementation of foo<int32_t> ?
When you've made an alias (using typedef or using) that alias is indistinguishable from the original type. You could consider making int24_t an enum though.
Example:
#include <cstdint>
#include <iostream>
enum int24_t : std::int32_t {};
template<typename T>
void foo(T v) {
std::cout << "base " << v << '\n';
}
template<>
void foo<std::int32_t>(std::int32_t v) {
std::cout << "int32_t " << v << '\n';
}
template<>
void foo<int24_t>(int24_t v) {
std::cout << "int24_t " << v << '\n';
}
int main() {
int24_t a{1};
std::int32_t b{2};
unsigned c{3};
foo(a);
foo(b);
foo(c);
a = static_cast<int24_t>(b); // needed to assign an int32_t to the enum type
foo(a);
}
Output
int24_t 1
int32_t 2
base 3
int24_t 2
Related
I am trying to make an entity component system with an interface like so:
int main() {
ecs::Registry registry;
// Add int, float, double components to the registry
registry.createComponents<int, float, double>();
// You don't have to supply all of the components at once
registry.createComponents<char>();
// Create an entity with int and float components, initialize them to 1 and 1.3f
ecs::Entity entity = registry.createEntity<int, float>(1, 1.3f);
// Add a double and a char component to the entity, set the double to 4.8
entity.addComponents<double, char>(4.8);
// Set the char component of entity to 'a'
// You don't have to set all the components in the addComponents function. Components not set will be default initialized
entity.set<char>('a');
// Access a component with entity.get<Component>()
std::cout << entity.get<int>() << std::endl;
std::cout << entity.get<float>() << std::endl;
std::cout << entity.get<double>() << std::endl;
std::cout << entity.get<char>() << std::endl;
// Remove given components
entity.removeComponents<int, float, double>();
// Print number of components
std::cout << "number of components: " << entity.componentCount() << std::endl;
// Char component still valid after other components deleted
std::cout << entity.get<char>() << std::endl;
// Delete entity
registry.deleteEntity(entity);
// Remove all given components from the registry
registry.deleteComponents<int, float, double, char>();
}
The Registry holds all the data, Entity merely holds its ID and a reference to the Registry. The problem is, inside the Registry, I want to have arrays of ints, floats, doubles, basically any trivial type. And I want to be able to access them through templated functions, and the only way I can envision this system working is by having an unordered_map with keys of type_index and vectors of the type specified by the type_index. But I can't have an unordered_map with differently templated values. I made a really dumb class that goes against every good C++ practice you could follow that would just hold the types using a vector of uint8_ts, but that is.. bad. Not good. Very messy code and an interface that makes me want to cry.
How do I have an unordered_map that holds contiguous, homogeneous containers of different trivial types?
You can hold each type in a separate unordered_map. That way you can support any data type.
Here's a possible implementation of Registry and Entity classes:
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <typeinfo>
#include <typeindex>
#include <memory>
struct icontainer
{
virtual ~icontainer() = default;
};
template <typename T>
struct container : icontainer
{
std::unordered_map<int, //entry_id
T> m_entryIdToValue; //value
};
struct Entity;
struct Registry
{
template <typename First>
void createComponents()
{
std::unique_ptr<icontainer> up{ new container<First> };
m_registeredContainers[std::type_index(typeid(First))] = std::move(up);
}
template <typename First, typename Second, typename... Types>
void createComponents()
{
createComponents<First>();
createComponents<Second, Types...>();
}
template <typename... Types>
Entity createEntity(Types ...values);
std::unordered_map<std::type_index, std::unique_ptr<icontainer>> m_registeredContainers;
std::size_t m_entityId = 0;
};
struct Entity
{
Entity(Registry *registry, std::size_t id) :m_registry(registry), m_nId(id){}
template <typename First>
void addComponents(const First &value)
{
get<First>() = value;
}
template <typename First, typename Second, typename... Types >
void addComponents(const First& first_value, const Second& second_value, Types ...values)
{
addComponents<First>(first_value);
addComponents<Second, Types...>(second_value, values...);
}
template <typename T>
T& get()
{
icontainer& c = *m_registry->m_registeredContainers[std::type_index(typeid(T))];
return static_cast<container<T>*>(&c)->m_entryIdToValue[m_nId];
}
std::size_t m_nId = 0;
Registry* m_registry = nullptr;
};
template <typename... Types>
Entity Registry::createEntity(Types ...values)
{
Entity e(this, m_entityId++);
e.addComponents(std::forward<Types>(values)...);
return e;
}
Here's a creation of 2 entities from a repository:
int main()
{
Registry r;
r.createComponents<int,float,double,char>();
Entity e = r.createEntity<int, float>(1,1.1f);
e.addComponents<double>(2.2);
std::cout << "entity 1 int " << e.get<int>() << std::endl;
std::cout << "entity 1 float " << e.get<float>() << std::endl;
std::cout << "entity 1 double " << e.get<double>() << std::endl;
Entity e2 = r.createEntity<int, char>(5, 'A');
std::cout << "entity 2 int " << e2.get<int>() << std::endl;
std::cout << "entity 2 char " << e2.get<char>() << std::endl;
return 0;
}
Output:
entity 1 int 1
entity 1 float 1.1
entity 1 double 2.2
entity 2 int 5
entity 2 char A
The code was compiled and tested on Visual Studio 2019.
In order to store a non-type template parameter inside the instance of a class, I often see either a static constexpr data member, or an enum declaration, as illustrated in the code below.
These two solutions seem equivalent when using them in simple case as in the main() function here.
This raises two questions:
1. Is there any operation we can do with one solution that we cannot do with the other?
2. Which versions of C++ allow or forbid these solutions?
#include <iostream>
// solution 1
template<int _i>
struct Foo
{
static constexpr int i = _i;
};
// solution 2
template<int _i>
struct Bar
{
enum
{
i = _i
};
};
int main()
{
Foo<1> foo;
Bar<2> bar;
std::cout << foo.i << std::endl;
std::cout << bar.i << std::endl;
std::cout << Foo<3>::i << std::endl;
std::cout << Bar<4>::i << std::endl;
return 0;
}
I have done a lot of research on this but I wasn't able to find a design pattern addressing the problem. This is a minimal description of what I'm trying to perform.
#include <iostream>
using namespace std;
template <class T, T default_value=T{}>
class A{
private:
T inclassValue;
public:
A(T icv):inclassValue{icv}{}
const T& operator[](int k){
if(k==1) return inclassValue;
return default_value;
}
};
struct two_int{int x;int y;};
int main(){
A<int> a{4};
cout << "a[0]=" << a[0] << endl;
cout << "a[1]=" << a[1] << endl;
/*
A<two_int> b{{3,5}};
cout << "b[0]=" << b[0].x << "," << b[0].y << endl;
cout << "b[1]=" << b[1].x << "," << b[1].y << endl;
*/
return 0;
}
The code will compile, link and output as expected
a[0]=0
a[1]=4
The compiler complains though and issues a warning for the line of code where default_value is used
return default_value;//Returning reference to local temporary object
which makes some sense. Uncommenting the last part in main and compiling, the compiler issue this time an error while building the template
template <class T, const T default_value= T{}>//A non-type template parameter cannot have type 'two_int'
while what I ideally hope for is
b[0]=0,0
b[1]=3,5
I was able to come up with a solution by adding an extra helper class, that will provide the default_value of T (as a static member), to the template arguments. I'm not convinced by the robustness of my trick and I was wondering if there exists a design pattern addressing this. The warning for types and the error for non-types. Also, I shall add that my primary goal is to be able to provide default_value at will (6 for int for example instead of 0).
Thanks
Not exactly sure what you're looking for, but perhaps a static helper finishing for creating a static default T could be useful:
template <typename T>
static const T& default_value() {
static const T* t = new T{};
return *t;
}
Note this will construct T at most once even across threads (in c++11), but still never destruct T. Since it's static, it's likely the lack of destruction is acceptable, but this of course depends on T.
Here is one version that forwards arguments to the constructor of a default_value stored as constexpr. You are quite limited here as to what is valid to pass as arguments (not sure exactly how limited) so it will depend on your use-case.
#include <iostream>
using namespace std;
template <class T, auto... Args>
class A{
private:
T inclassValue;
constexpr static T default_val = T{Args...}; // Changed to curly brackets here
public:
constexpr A(T icv):inclassValue{icv}{}
const T& operator[](int k){
if(k==1) return inclassValue;
return default_val;
}
};
struct two_int{int x;int y;};
int main(){
A<int> a{4};
cout << "a[0]=" << a[0] << endl;
cout << "a[1]=" << a[1] << endl;
A<two_int> b{{3,5}};
cout << "b[0]=" << b[0].x << "," << b[0].y << endl;
cout << "b[1]=" << b[1].x << "," << b[1].y << endl;
return 0;
}
Minimal working example:
#include <tuple>
struct example
{
example(int, char) {}
};
int main()
{
std::tuple<example, int, double>
my_tuple(example(0, 'x'), 42, .0);
// std::tuple t = make_my_tuple(0, 'x');
return 0;
}
This works.
Is there a more elegant way to initialize only the first member, like I sketched in the comment? One which only takes the arguments to construct the first tuple member and does not initialize the others?
The reason I ask? I am just interested in the semantics of the language.
You say that giving values for the other two members is not necessary - are you worried about performance? Or that there may be no suitable value for these members?
If it's the latter, you could have your tuple hold boost::optionals. e.g.
#include <tuple>
#include <boost/optional.hpp>
using namespace boost;
struct example
{
example(int, char) {}
};
typedef std::tuple<example, optional<int>, optional<double>> MyTuple;
int main()
{
MyTuple my_tuple(example(0, 'x'), optional<int>(), optional<double>());
return 0;
}
You now semantically have the int and float "uninitialised", and can query their value as such.
To make this more elegant, you can wrap this into a function, using the perfect forwarding idiom for the arguments (in general; in this case your arguments are cheap to copy, so no speed benefit from doing this):
template <class... Args>
MyTuple make_mytuple(Args&&... args)
{
return MyTuple(example(std::forward<Args>(args)...), optional<int>(), optional<double));
}
The advantage of this template is that it's resilient to changes in example's constructor. If you add another argument, just call make_mytuple with the new arguments and it will work.
Your other point about the copying in the tuple construction is valid, but in reality I believe this will be optimal on most compilers. (a combination of RVO and elision of copies when passing an rvalue to a function by value).
You can use uniform initialization. Sadly, you cannot define a default value, argument will be initialized with the default constructor or 0.
#include <iostream>
#include <tuple>
enum class Result {Full, Partial, Empty};
std::tuple<bool, int, double> get_tuple(Result type)
{
if (type == Result::Full)
return {true, 42, 3.14159};
else if (type == Result::Partial)
return {true, 42, {}};
else
return {};
}
int main()
{
bool b;
int i;
double d;
std::tie(b, i, d) = get_tuple(Result::Full);
std::cout << b << " " << i << " " << d << std::endl;
std::tie(b, i, d) = get_tuple(Result::Partial);
std::cout << b << " " << i << " " << d << std::endl;
std::tie(b, i, d) = get_tuple(Result::Empty);
std::cout << b << " " << i << " " << d << std::endl;
return 0;
}
output:
1 42 3.14159
1 42 0
0 0 0
First of all I want a normal enumeration instead of a bit-based enumeration, because the amount of different enums will be beyond any integral type. I also want to take advantage of the type safety of C++11 enum class. To do so, the natural choice would be std::bitset, however I have no idea how to bind those two together.
A custom bitset would be needed? How to go around the implementation of such a class?
Since enum classes are wrappers for enums, you can cast them to underlying type. And using some private inheritance you can selectively import some functionalities from C++ stdlib classes without worrying about Liskov's principle. Composition resulted in clearer code. Using these functionalities, we can wrap std::bitset. Following code contains only subset of functionalites, but it can be expanded further.
There's a problem with max value - that you can't get maximum value of enum class (or am I wrong?). So I added EnumTraits. Now users are required to specialize EnumTraits with const value max equal to the max value of enum before class can be used.
#include <bitset>
#include <type_traits>
template<typename T>
struct EnumTraits;
template<typename T>
class EnumClassBitset
{
private:
std::bitset<static_cast<typename std::underlying_type<T>::type>(EnumTraits<T>::max)> c;
typename std::underlying_type<T>::type get_value(T v) const
{
return static_cast<typename std::underlying_type<T>::type>(v);
}
public:
EnumClassBitset() : c()
{
}
bool test(T pos) const
{
return c.test(get_value(pos));
}
EnumClassBitset& reset(T pos)
{
c.reset(get_value(pos));
return *this;
}
EnumClassBitset& flip(T pos)
{
c.flip(get_value(pos));
return *this;
}
};
enum class BitFlags
{
False,
True,
FileNotFound,
Write,
Read,
MaxVal
};
template<>
struct EnumTraits<BitFlags>
{
static const BitFlags max = BitFlags::MaxVal;
};
#include <iostream>
int main()
{
EnumClassBitset<BitFlags> f;
f.flip(BitFlags::True);
f.flip(BitFlags::FileNotFound);
//f.flip(2); //fails to compile
std::cout << "Is False? " << f.test(BitFlags::False) << "\n";
std::cout << "Is True? " << f.test(BitFlags::True) << "\n";
std::cout << "Is FileNotFound? " << f.test(BitFlags::FileNotFound) << "\n";
std::cout << "Is Write? " << f.test(BitFlags::Write) << "\n";
std::cout << "Is Read? " << f.test(BitFlags::Read) << "\n";
}
Since enums don't have much functionality unfortunately, and what is more, C++11 with enum classes don't improve the situation, some programmers use static map wrapped in a class. Definitely a good read.