I have a C++ class that might work with linear or binary search.
Currently I have additional member variable that show what search to be used.
I know how I can do the search with Java-like virtual functions (strategy pattern or template method),
but I am curious what is best practice this to be done compile time with templates<>?
Current (simplified) code looks like this:
int MyArray::lookup(const String &key) const{
if (lookupMethod == LINEAR_SEARCH)
return lookupLinearSearch(key);
else
return lookupBinarySearch(key);
}
The most obvious way is to encapsulate the strategies in types and implement lookup() as a function template:
namespace searchStrategy {
struct linearSearch {
static auto lookup(const MyArray& a, const String &key)
-> int {
return a.lookupLinearSearch(key);
};
};
struct binarySearch {
static auto lookup(const MyArray& a, const String &key)
-> int {
return a.lookupBinarySearch(key);
};
};
}
class MyArray {
public:
template <typename LookupStrategy>
auto lookup(const String &key) const
-> int {
return LookupStrategy::lookup(*this, key);
};
};
// ...
auto myArray = MyArray{};
mArray.lookup<searchStrategy::binarySearch>("asdf"s);
Alternatively you can specialize the template and use the strategy type as tag only:
namespace searchStrategy {
struct linearSearch {};
struct binarySearch {};
}
class MyArray {
public:
template <typename LookupStrategy>
int lookup(const String &key) const;
template <>
int lookup<searchStrategy::linearSearch>(const String &key) const {
return lookupLinearSearch(key);
};
template <>
int lookup<searchStrategy::binarySearch>(const String &key) const {
return lookupBinarySearch(key)
};
};
// ...
auto myArray = MyArray{};
mArray.lookup<searchStrategy::binarySearch>("asdf"s);
Now you might prefer to not have to define the template parameters, but use a function parameter instead. You don't even need templates for that, but just method overloading:
namespace searchStrategy {
struct LinearSearchT {};
struct BinarySearchT {};
static const LinearSearchT linearSearch;
static const BinarySearchT binarySearch;
}
class MyArray {
public:
int lookup(const String &key,
const searchStrategy::linearSearchT strategy) const {
return lookupLinearSearch(key);
};
int lookup(const String &key,
const searchStrategy::binarySearchT strategy) const {
return lookupBinarySearch(key)
};
};
// ...
auto myArray = MyArray{};
mArray.lookup("asdf"s, searchStrategy::binarySearch);
Looks like dynamically choosing the strategy, but is static. You can not do something like this:
mArray.lookup("asdf"s, (someReason) ?
searchStrategy::binarySearch :
searchStrategy::linearSearch);
In that case you'd have to write
if( someReason )
mArray.lookup("asdf"s, searchStrategy::binarySearch);
else
mArray.lookup("asdf"s, searchStrategy::linearSearch);
Beware: all code in this answer is untested. It will contain bugs.
You can use template specialization, provided lookupMethod is known at compile time.
For example, you can do it like this:
template <int METHOD>
int MyArray::lookup(const String& key); // Not implemented
template<>
int MyArray::lookup<LINEAR_SEARCH>(const String& key) {
return lookupLinearSearch(key);
}
template<>
int MyArray::lookup<BINARY_SEARCH>(const String& key) {
return lookupBinarySearch(key);
}
If you are willing to always commit to which strategy you are going to use at compile time, you can do this:
enum class METHOD {LINEAR_SEARCH, BINARY_SEARCH};
template <METHOD M>
int MyArray::lookup(const String & key) {
if (M == METHOD::LINEAR_SEARCH)
return lookupLinearSearch(key);
else
return lookupBinarySearch(key);
}
Related
I am using a library which define various constants, like JQX_TYPE_INT, JQX_TYPE_LONG, JQX_TYPE_DOUBLE etc.
My problem is to relate values to c++ types.
For example, in order to have a function which works for various types i have to implement a copy of the same code, differing only in the constant, for every c++-type, e.g.:
myObj *createObject(int iSize, int iDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_INT);
return new myObj(iRealSize);
}
myObj *createObject(int iSize, double dDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_DOUBLE);
return new myObj(iRealSize);
}
// ... same for float char, etc.
That is, apart from having to implement the same code several times, i have to use a dummy variable in order to use the constant related to the c++-type.
Is there some sort of template approach to do something like
template<tyepename T>
struct jqx_type {
static const int type;
// ... template magic
}
template<<typename T>
myObj *createObject(int iSize) {
int iRealSize = calcEffectiveSize(iSize, jqx_type<T>::type);
return new myObj(iRealSize);
}
I tried this
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
But this doesn't even compile.
Is there a way to achieve what i intend to do?
Here:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
You redeclared the same template several times with different default arguments. What you actually want is one template with different specializations:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
PS: type is very common name for member type aliases. I would not use it for an int. Maybe type_selector is a better name.
I would go with a much simpler
myObj *createObject(int iSize, int jqx_type) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject(16, JQX_TYPE_DOUBLE);
or if it needs to be constexpr
template <int jqx_type>
myObj *createObject(int iSize) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject<JQX_TYPE_DOUBLE>(16);
but lets assume you need the actual type it could be something like this:
template <typename T>
struct MyObj {
MyObj(std::size_t size, int jqx_type)
: iSize{calcEffectiveSize(size, jqx_type)} { ... }
MyObj * create(std::size_t size) {
constexpr int jqx_type = []() {
if constexpr (std::is_same_v<T, int>) return JQX_TYPE_INT;
if constexpr (std::is_same_v<T, double>) return JQX_TYPE_DOUBLE;
}();
return new MyObj{iSize, jqx_type};
}
};
or if you do want to go the type traits route then you have to specialize the template struct like this:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
But that's as much code as specializing the createObject function.
PS: you can match and mix ideas, too. E.g. template createObject function with the if constexpr lambda.
I am trying to create a templated wrapper class on stl unordered_map. I am passing the hash function class as a template parameter to the wrapper class and provided a string specialization. The below code compiles and works but if the commented part is included then compilation error saying:
"/usr/include/c++/6/bits/unordered_map.h:143:28: error: no matching
function for call to ‘HashFunction
::HashFunction()’
const hasher& __hf = hasher(),".
However, I am bound to have the ctor of the hash function class. I tried various ways but could not make it work. Please provide your thoughts/comments.
#include <iostream>
#include <unordered_map>
using namespace std;
template< class Key >
class HashFunction
{
public:
//HashFunction( const Key & inKey );
size_t operator()(const Key &inKey) const;
private:
unsigned mHashCode;
};
//template<>
//HashFunction< string >::HashFunction( const string & inKey )
//{
//mHashCode=std::hash<string>{}(inKey);
//}
template <>
size_t HashFunction< string >::operator()(const string &inKey) const
{
return std::hash<string>{}(inKey);
//return mHashCode;
}
template< class Key, class Val, class Hash = HashFunction< Key > >
class unordered_map_wrapper
{
public:
unordered_map_wrapper();
private:
unordered_map<Key, Val, Hash> * mTable;
};
template< class Key, class Val, class Hash >
unordered_map_wrapper< Key, Val, Hash >::unordered_map_wrapper()
{
mTable=new unordered_map<Key, Val, Hash>(10);
}
int main() {
unordered_map_wrapper<string, unsigned> h;
return 0;
}
This is not how the Hash template parameter is intended to work in std::unordered_map! The map creates one single Hash instance (using the default constructor, so you need to provide it!) which is used for all hash calculations needed further. So your class must look like this instead:
template<class Key>
class HashFunction
{
public:
// HashFunction(); <- can just leave it out...
size_t operator()(const Key& inKey) const;
};
template<>
size_t HashFunction<std::string>::operator()(const std::string& inKey) const
{
// calculate hash value in operator!!!
return std::hash<std::string>()(inKey);
}
To avoid constructing the std::hash all the time, though, I'd rather specialise the whole template class instead:
template<class Key>
class HashFunction; // leave entirely unimplemented
// (if it has no general meaning, at least...)
template<>
class HashFunction<std::string>
{
public:
size_t operator()(const std::string& inKey) const
{
return mHash(inKey);
}
private:
std::hash<std::string> mHash;
};
It does not make much sense re-implementing something that is already there, though. May I assume that you used std::string here just as a place holder for your own class?
By the way: A simple typedef would do the job easier than your wrapper class:
template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction<Key>>;
Maybe you are interested in alternatives, too? Lets first assume you have two classes (instead of std::string):
class C1
{ };
class C2
{ };
Most elegant solution (in my eyes at least) is simply specialising std::hash for your classes (while in general, it is illegal to add something to std namespace, template specialisations are the exception...):
namespace std
{
template<>
class hash<C1>
{
public:
size_t operator()(C const& c) const
{
return 0; // your implementation here...
}
};
// same for C2
}
// no typedef necessary, just use std::unordered_map directly:
std::unordered_map<C1, unsigned> m1;
std::unordered_map<C2, unsigned> m2;
You can provide your own HashFunction class, providing overloads:
class HashFunction
{
public:
size_t operator()(C1 const& c) const
{
return 0;
}
size_t operator()(C2 const& c) const
{
return 0;
}
};
Again, a typedef makes your life easier:
template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction>;
std::unordered_map<C1, unsigned, HashFunction> m1;
my_unordered_map<C1, unsigned> m1_;
std::unordered_map<C2, unsigned, HashFunction> m2;
my_unordered_map<C2, unsigned> m2_;
Finally, if you absolutely need to initialise your HashFunction with some parameters to configure it correctly, you can do so, but you have to deliver a pre-configured instance to your hash map then!
template<typename T>
class HashFunction
{
public:
HashFunction(double value);
size_t operator()(T const& c) const;
private:
double data;
};
template<typename T>
HashFunction<T>::HashFunction(double value)
: data(value)
{ }
template<>
size_t HashFunction<C1>::operator()(C1 const& c) const
{
return static_cast<size_t>(data);
};
template<>
size_t HashFunction<C2>::operator()(C2 const& c) const
{
return static_cast<size_t>(data);
};
template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction<Key>>;
my_unordered_map<C1, unsigned> m1(12, HashFunction<C1>(10.12));
my_unordered_map<C2, unsigned> m2(10, HashFunction<C2>(12.10));
Well, now, at least, your wrapper class can come in handy again:
template <typename Key, typename Value, typename Hash = HashFunction<Key>>
class unordered_map_wrapper
{
public:
unordered_map_wrapper()
: mMap(16, Hash())
{ }
unordered_map_wrapper(double parameter)
: mMap(16, Hash(parameter))
{ }
private:
std::unordered_map<Key, Value, Hash> mMap;
};
unordered_map_wrapper<C1, unsigned> m1(10.12);
unordered_map_wrapper<C2, unsigned> m2(12.10);
// possible due to provided default ctor:
unordered_map_wrapper<std::string, unsigned, std::hash<std::string>> m3;
It expects to have a default constructor, which is removed when you define your custom version.
This compiles:
template< class Key >
class HashFunction
{
public:
HashFunction(){};
HashFunction( const Key & inKey );
size_t operator()(const Key &inKey) const;
private:
unsigned mHashCode;
};
For many classes in my codebase, I need to maintain a global map allowing me to look up instances of that class.
Example:
// In ExampleClass.h
ExampleClass *getExampleClass(std::string name);
std::unordered_map<std::string, ExampleClass *> &getAllExampleClasses();
bool addExampleClass(std::string name, ExampleClass *e);
// In ExampleClass.cpp
std::unordered_map<std::string, ExampleClass *> exampleClasses;
ExampleClass *getExampleClass(std::string name) {
return exampleClasses.find(name) == exampleClasses.end() ? nullptr : exampleClasses.find(name)->second;
}
std::unordered_map<std::string, ExampleClass *> &getAllExampleClasses() {
return &exampleClasses;
}
bool addExampleClass(std::string name, ExampleClass *e) {
if (exampleClasses.find(name) == exampleClasses.end()) {
exampleClasses[name] = e;
return true;
}
else
return false;
}
After doing this for several classes, I decided to put all of these global variables and methods in global.cpp/h and use template functions.
template <class T> T *get(std::string name);
template <class T> std::unordered_map<std::string, T *> &getAll();
template <class T> bool add(std::string name, T *);
Is there a way in the template function definition to specify which global map to lookup based on the type? So that get("name") will know to check the exampleClasses map?
There are two questions in your question, I will address the first. What you likely want is to group all of your functions as statics in a single class template. Note that your implementations of your functions are very inefficient, so I've fixed that for you too:
template <typename T>
using RegistryMap = std::unordered_map<std::string, T*>;
template <typename T>
class Registry {
static RegistryMap<T> registry;
public:
static T* get(const std::string& name) {
auto it = registry.find(name);
return it == registry.end() ? nullptr : it->second;
}
static const RegistryMap<T>& getAll() {
return registry;
}
static bool add(const std::string& name, T* object) {
T*& store = registry[name];
if (store) {
return false;
}
else {
store = object;
return true;
}
}
};
template <typename T>
RegistryMap<T> Registry<T>::registry;
For your original example, you would this as:
Registry<Example>::get("name", &some_example);
I am trying to do something like this:
template<typename enumType,
std::initializer_list<enumType> values,
std::initializer_list<std::string> mappings>
struct enum_converter {
enumType toEnum(const std::string& literal) { ... }
std::string toString(const enumType value) { ... }
};
I want to use it as follows:
enum test_enum {value_a, value_b};
struct test_enum_converter : public enum_converter<
test_enum,
{value_a, value_b},
{"a", "b"}> {};
GCC tells me:
class std::initializer_list<_Tp> is not a valid type
for a template constant parameter.
Adding const to the type does not change anything. Is there a workaround or a similar solution?
Only integral types, enumerations, pointers and references are allowed as non-type template parameters. std::initializer_list is neither of those.
Do you need the values & mappings to be template parameters? How about making them normal parameters of the constructor, and keeping only the enum as a template param?
template<typename enumType>
struct enum_converter {
enum_converter(std::initializer_list<enumType> values, std::initializer_list<std::string> mappings) : values(values), mappings(mappings)
enumType toEnum(const std::string& literal) { ... }
std::string toString(const enumType value) { ... }
private:
std::initializer_list<enumType> values;
std::initializer_list<std::string> mappings;
};
enum_converter<test_enum> test_enum_converter({...}, {...});
int main()
{
test_enum_converter.toEnum("bla");
}
EDIT
Here is an instance-free alternative:
template<typename enumType>
struct enum_converter {
static init(std::initializer_list<enumType> values, std::initializer_list<std::string> mappings)
{ s_values = values; s_mappings = mappings; }
static enumType toEnum(const std::string& literal) { ... }
static std::string toString(const enumType value) { ... }
private:
static std::initializer_list<enumType> s_values;
static std::initializer_list<std::string> s_mappings;
};
Just call init() once before using the class.
If you need more than one instantiation for a particular enumeration, you can add a disambiguation parameter to the template, like this:
template <typename enumType, typename tag>
struct enum_converter { /*as before*/ };
int main()
{
struct data_strings;
struct user_readable_strings;
enum_converter<test_enum, data_strings>::init({...}, {"a", "b"});
enum_converter<test_enum, user_readable_strings>::init({...}, {"Option A", "Option B"});
}
Pointers can only be template arguments if they are constant expressions, i.e. the address of an object with extern linkage. The following code works, though it's your call whether it's worth it:
typedef char const * charptype;
template <int, charptype> struct item;
template <typename ...> struct econv
{
static constexpr charptype convert(int) { return nullptr; }
};
template <int I, charptype S, typename ...Tail>
struct econv<item<I, S>, Tail...>
{
static constexpr charptype convert(int i)
{
return i == I ? S : econv<Tail...>::convert(i);
}
};
#include <iostream>
extern char const Hello[] = "Hello";
extern char const World[] = "World";
int main()
{
std::cout << econv< item<1, Hello>
, item<2, World> >::convert(2)
<< "\n";
}
By putting the strings into an anonymous namespace and using some helper macros, this approach might be cleaned up a bit to look presentable.
Currently, I have some code as follows
template<typename Type>
Type* getValue(std::string name, bool tryUseGetter = true)
{
if(tryUseGetter)
{
if(_properties[name]->hasGetter)
{
return (Type*)_properties[name]->getter();
}
return (Type*)_properties[name]->data;
}
else
{
return (Type*)_properties[name]->data;
}
}
Is there a way to make tryUseGetter a compile time switch? i.e. move it to the template declaration so it's something akin to this
template<typename Type, bool tryUseGetter = true>
...
Thanks.
As an alternative to Dirk's answer, you can put the function in a struct. Template classes can be partially specialized (in contrast to template functions), so you can write:
template<typename Type, bool tryUseGetter = true>
struct getValue;
template<typename Type>
struct getValue<Type, true>
{
Type* run(std::string name)
{
if(_properties[name]->hasGetter)
{
return (Type*)_properties[name]->getter();
}
return (Type*)_properties[name]->data;
}
};
template<typename Type>
struct getValue<Type, false>
{
Type* run(std::string name)
{
return (Type*)_properties[name]->data;
}
};
Call it as getValue<T>::run("foo") or getValue<T, false>::run("foo") .
I'm not 100% certain that it's allowed to have template parameters of the type bool, so perhaps you should change it to int.
You can get compile-time dispatch of the "try-use-getter" stuff by splitting your method into two and having the compiler dispatch to the appropriate method:
struct __try_use_getter { }
external const __try_use_getter tryusegetter;
template<typename Type>
Type*
getValue(std::string name, const __try_use_getter&)
{
if(_properties[name]->hasGetter)
{
return (Type*)_properties[name]->getter();
}
return (Type*)_properties[name]->data;
}
template<typename Type>
Type*
getValue(std::string name)
{
return (Type*)_properties[name]->data;
}
With this scenario in place, you would have full compile-time dispatching:
int result = getValue("foo", tryusegetter);
would try the getter first, whereas
int result = getValue("foo");
would immediately call the getter-less version.
Just in case you really need it (although from a performance point of view, i doubt it would be noticeable), i would overload
template<typename Type>
Type* getValue(std::string const &name)
{
if(_properties[name]->hasGetter)
{
return (Type*)_properties[name]->getter();
}
return (Type*)_properties[name]->data;
}
template<typename Type, bool tryUseGetter>
Type *getValue(std::string const &name)
{
if(tryUseGetter)
{
return getValue<Type>(name);
}
else
{
return (Type*)_properties[name]->data;
}
}
Also, you should first follow the real rules: Pass name by-const-reference instead of passing a copy, for example.
You could use type cast operator and structure getValue as follows (usage syntax will be the same as with function) :
template<typename Type, bool tryUseGetter = true>
struct getValue {};
template<typename Type>
struct getValue<Type, true> {
getValue(const std::string& name) : name(name) {};
operator Type*() const {
if(_properties[name]->hasGetter) {
return (Type*)_properties[name]->getter();
}
return (Type*)_properties[name]->data;
}
private:
const std::string& name;
};
template<typename Type>
struct getValue<Type, false> {
getValue(const std::string& name) : name(name) {};
operator Type*() const {
return (Type*)_properties[name]->data;
}
private:
const std::string& name;
};
Usage:
int main () {
int* i = getValue<int>( "TEST" ); // true by default
Xstruct* x = getValue<Xstruct, false>( "XS" ); // false
}
Before you go and make the code all complicated...did you check to see if the optimizing compiler was already doing this for you?