I have a sub-class that has to implement a virtual function so that callers can interface with it without knowing its concrete type. But I'd like to template this sub-class to work with different types. Basically, this:
class Base
{
public:
virtual void insert(const std::string & val, const std::string & type_str) = 0;
};
template<typename T>
class C : public Base
{
public:
virtual void insert(const std::string & val, const std::string & type_str) final
{
// Something that tries to convert val to type T and inserts into container;
// Would throw if conversion fails
// e.g.:
if (type_str == "int")
container.insert(std::stoi(val));
else if (type_str == "str")
container.insert(val); // Error: No matching function call to `insert` [...]
}
private:
std::set<T> container;
};
I understand the problem: any given specialization of C won't have a container that accepts types other than T; it's only a runtime assurance that nobody calls C::insert() with the wrong type.
Is there any way around this? I'd like to keep the same interface for this sub-class as I have with others that inherit from Base, which requires accepting a string and doing the conversion internally.
More than happy to have a C++20 solution (had some failed attempts using concepts to solve this...). And if it matters, I'm using gcc.
You can use if constexpr (C++17 and later):
#include <type_traits>
class Base
{
public:
virtual void insert(const std::string & val, const std::string & type_str) = 0;
};
template<typename T>
class C : public Base
{
public:
virtual void insert(const std::string & val, const std::string & type_str) final
{
if constexpr(std::is_same_v<T,int>){
assert(type_str == "int"); // or you can just drop this, depend on what you want
container.insert(std::stoi(val));
}
else if constexpr(std::is_same_v<T,std::string>){
assert(type_str == "str");
container.insert(val);
}
else{
static_assert(!std::is_same_v<T,T>,"not supported conversion");
}
}
private:
std::set<T> container;
};
Or, you can use template specialization instead.
For a C++20 concepts solution, you could factor the insert logic out into helper functions and add requires clauses to make sure T is convertible to the type given by type_str at runtime. For example:
template <typename T>
T parse(const std::string& val, const std::string& type_str)
requires std::convertible_to<T, int>
{
if (type_str != "int") {
throw std::runtime_error("can't convert to int");
}
return std::stoi(val);
}
template <typename T>
T parse(const std::string& val, const std::string& type_str)
requires std::convertible_to<T, std::string>
{
if (type_str != "str") {
throw std::runtime_error("can't convert to str");
}
return val;
}
template<typename T>
class C : public Base
{
public:
virtual void insert(const std::string & val, const std::string & type_str) final
{
container.insert(parse<T>(val, type_str));
}
private:
std::set<T> container;
};
Demo
Related
I have a question regarding the std::unordered_map and a custom class as it's key.
I think some background is required first:
The custom class is a variant data type, which implements basic numerical types and the std::string class.
Recently a bro of mine told me that it would be nice if the class supported arrays and hashtables. "Say no more" I thought and started implementing the array functionality (using std::vector) which works really great and then I implemented the hashmap functionality (using unordered_map<Variant, Variant>).
If I understand it right the hash function (or operator() respectively) for the unordered_map has to comply to the signature size_t (*) (const Key_Type &k) const; which my specialized version of the std::hash<Variant> object should do, shouldn't it?
In addition the unordered_map needs to check Key_Type for equality, which should be possible via operator==(), am I correct?
Anyway I'm getting a load of beautiful compiler errors of which this is, in my opinion, the most helpful:
/usr/include/c++/4.9/bits/hashtable_policy.h:85:33: error: no match for call to ‘(const std::hash<Variant>) (const Variant&)’
I really don't understand what's going on and would be really grateful for any insights in what's going on.
Below is a stripped down header of the class Variant, I hope enough information is included (to be honest I fear it's too much information but I was not sure what could be omitted).
But I left out most of the implementation details since the problem seems to occur only in the specialized hash object.
Well this is the stripped down version of the Variant header:
class Variant
{
private:
enum Type {NONE = 0, LONG, DOUBLE, STRING, ARRAY, HASH_MAP};
using Var = struct Var
{
union
{
int64_t l;
double d;
std::string *s;
std::vector<Variant> *v;
std::unordered_map<Variant, Variant> *h;
};
Type type = NONE;
};
public:
//constructors, destructor and clear function
Variant() : var() {}
Variant(long val): Variant(){var.type = LONG; var.l = val;}
Variant(double val) : Variant(){var.type = DOUBLE; var.d = val;}
Variant(const std::string &val) : Variant(){var.type = STRING; var.s = new std::string(val);}
template<typename T, typename... Args>Variant(T val, Args... args) : Variant() {set(val, args...);} //constructs an array
Variant(const Variant &val); //calls default constructor as well
Variant(Variant &&val) : Variant() {swap(*this, val);}
~Variant(){clear();}
void clear();
//set functions
template<typename T, typename... Args> void set(const T val, Args... args){if(var.type == ARRAY)var.v->clear();add(val, args...);}
void set(long val);
void set(double val);
void set(const std::string &val);
void set(const Variant &val);
//add functions
template<typename T> void add(const T val){add(Variant(val));}
template<typename T, typename... Args> void add(const T val, Args... args){add(Variant(val)); add(args...);}
void add(const std::string &val){add(Variant(val));}
void add(const Variant &val);
//array access and evaluation functions
Variant& operator[](const Variant &idx);
size_t size() const {if(var.type == ARRAY)return var.v->size(); return 0;}
std::unordered_map<Variant, Variant>::iterator begin(){if(var.type == HASH_MAP)return var.h->begin(); throw Exception("The internal type does not support iterators");}
//operator= definitions
template<typename T> Variant& operator=(const T val){set(val); return *this;}
Variant& operator=(const std::string &val){set(val); return *this;}
Variant& operator=(Variant val){swap(*this, val); return *this;}
//operator definitions
Variant& operator+=(const Variant &right);
//and operator-=, ^= etc etc...
//friend definitions (mainly comparison operators)
friend void swap(Variant &left, Variant &right); //simple swap function
friend bool operator==(const Variant &left, const Variant &right);
friend bool operator!=(const Variant &left, const Variant &right);
friend std::hash<Variant>;
private:
Var var;
};
template <typename T>
inline void hash_combine(std::size_t& seed, const T &v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
namespace std
{
template<> struct hash<Variant>
{
size_t operator()(const Variant &x) const
{
if(x.var.type == Variant::DOUBLE)
return std::hash<double>()(x.var.d);
else if(x.var.type == Variant::LONG)
return std::hash<int64_t>()(x.var.l);
else if(x.var.type == Variant::STRING)
return std::hash<std::string>()(*x.var.s);
else if(x.var.type == Variant::ARRAY)
{
size_t seed = 0;
for(size_t i = 0; i < x.var.v->size(); ++i)
hash_combine(seed, x.var.v->operator[](i));
return seed;
}
else if(x.var.type == Variant::HASH_MAP)
{
size_t seed = 0;
for(auto it = x.var.h->begin(); it != x.var.h->end(); ++it)
{
hash_combine(seed, it->first);
hash_combine(seed, it->second);
}
return seed;
}
else if(x.var.type == Variant::NONE)
return 0;
else
throw std::runtime_error("This Variant cannot be hashed");
}
};
}
inline void swap(Variant &left, Variant &right){Variant::Var tmp(left.var); left.var = right.var; right.var = tmp;}
bool operator==(const Variant &left, const Variant &right);
bool operator!=(const Variant &left, const Variant &right);
The problem here is that you use unordered_map<Variant, Variant> inside the definition of Variant itself. At this point your hash specialization is not available yet, that's why the compiler produces the error. You cannot just move hash definition before Variant definition because the hash needs access to Variant members. What you can do, is to separate declaration and definition of your hash:
class Variant;
namespace std
{
template<> struct hash<Variant>
{
size_t operator()(const Variant & x) const;
};
}
class Variant {
/* Variant definition goes here ... */
};
template <typename T>
inline void hash_combine(std::size_t& seed, const T &v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
size_t std::hash<Variant>::operator()(const Variant &x) const
{
/* hash function implementation here ... */
}
But then you have another problem: inside Variant class definition the Variant itself is incomplete type. In your union you store only pointers to vector and unordered_map, this is OK, but the begin method (actually, specification of its return type already) requires an instantiation of the unordered_map<Variant, Variant> which is not possible at that place.
(Note: Limited support for containers of incomplete types (only vector, list, and forward_list) will be added to C++17)
To solve this second problem, you can have a map member function instead of begin function which gives access to the internal map:
std::unordered_map<Variant, Variant> & map()
{
if (var.type == HASH_MAP)
return *var.h;
throw Exception("The internal type does not support iterators");
}
Then instead of
Variant v;
v.begin();
you would use
v.map().begin();
I am trying to wrap some templated functions into some binary functors like below. When I try to compile the code I have the error
error: no match for call to ‘(QtyAsc) (myobj&, myobj&)
I thought that being operator() in QtyAsc a function in a class, the template deduction mechanism would have worked but it seems that the compiler doesn't accept myobj classes as valid types for it.
Is it maybe because of the call to boost::bind? I was trying to provide a default implementation for the second templated argument (unfortunately I cannot use C++11 with default templated arguments).
class myobj {
public:
myobj(int val) : qty_(val) {}
int qty() { return qty_;}
private:
int qty_;
};
template<class T>
int get_quantity(const T& o) {
throw runtime_error("get_quantity<T> not implemented");
}
template<>
int get_quantity(const myobj& o) {
return o.qty();
}
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr = boost::bind(&get_quantity<T>,_1)) const {
if(extr(o1) < extr(o2))
return true;
return false;
}
};
int main() {
myobj t1(10),t2(20);
QtyAsc asc;
if(asc(t1,t2))
cout << "Yes" << endl;
}
If you can't use C++11, just provide an additional overload:
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr) const {
return extr(o1) < extr(o2);
}
template<class T>
bool operator()(const T& o1, const T& o2) const {
return operator()(o1, o2, &get_quantity<T>);
}
};
(I've omitted the unnecessary boost::bind.) Also, you will need to declare myobj::qty to be const:
int qty() const {
return qty_;
}
since you want to invoke it on const objects. (Live demo)
To narrow it down: I'm currently using Boost.Unordered. I see two possible solutions:
Define my own Equality Predicates and Hash Functions and to utilize templates (maybe is_pointer) to distinct between pointers and instances;
Simply to extend boost::hash by providing hash_value(Type* const& x) as for hashing; and add == operator overload as free function with (Type* const& x, Type* const& y) parameters as for equality checking.
I'm not sure whether both variations are actually possible, since I didn't test them. I would like to find out you handle this problem. Implementations are welcome :)
EDIT 1:
What about this?
template<class T>
struct Equals: std::binary_function<T, T, bool> {
bool operator()(T const& left, T const& right) const {
return left == right;
}
};
template<class T>
struct Equals<T*> : std::binary_function<T*, T*, bool> {
bool operator()(T* const& left, T* const& right) const {
return *left == *right;
}
};
EDIT 2:
I've just defined:
friend std::size_t hash_value(Base const& base) {
boost::hash<std::string> hash;
return hash(base.string_);
}
friend std::size_t hash_value(Base* const& base) {
return hash_value(*base);
}
And then:
Derived d1("x");
Derived d2("x");
unordered_set<Base*> set;
set.insert(&d1);
assert(set.find(&d2) == end());
Debugger says that friend std::size_t hash_value(Base* const& base) is never called (GCC 4.7). Why is that?
EDIT 3:
I found out that template <class T> std::size_t hash_value(T* const& v) in boost/functional/hash.hpp on line #215 (Boost 1.49) is Boost's specialization for pointers and it simply masks your custom implementation of hash_value such as mine in EDIT 2.
Therefore, it seems like the only way here is to create a custom Hash Functor.
For the hash function, you have a choice between specializing boost::hash (or std::hash in the newer standard) or defining a new functor class. These alternatives work equally well.
For the equality operator, you need to define a new functor, because you cannot redefine the equality operator over pointers. It's a built-in operator (defined in functional terms as bool operator==( T const *x, T const *y )) and cannot be replaced.
Both of these can be defined generically by using a templated operator() in a non-templated class.
struct indirect_equal {
template< typename X, typename Y >
bool operator() ( X const &lhs, Y const &rhs )
{ return * lhs == * rhs; }
};
Follow a similar pattern for the hasher.
Taking into consideration all edits in the original post I would like to provide complete solution which satisfies my needs:
1. Equality:
template<class T>
struct Equal: ::std::binary_function<T, T, bool> {
bool operator()(T const& left, T const& right) const {
::std::equal_to<T> equal;
return equal(left, right);
}
};
template<class T>
struct Equal<T*> : ::std::binary_function<T*, T*, bool> {
bool operator()(T* const & left, T* const & right) const {
Equal<T> equal;
return equal(*left, *right);
}
};
2. Hashing:
template<class T>
struct Hash: ::std::unary_function<T, ::std::size_t> {
::std::size_t operator()(T const & value) const {
::boost::hash<T> hash;
return hash(value);
}
};
template<class T>
struct Hash<T*> : ::std::unary_function<T*, ::std::size_t> {
::std::size_t operator()(T* const & value) const {
Hash<T> hash;
return hash(*value);
}
};
So now I can continue using Boost's hash_value and it will not get masked for pointer types by Boost's default implementation (see EDIT 3).
3. Example:
In my application I have a thin wrapper for unordered_set which now looks like that:
template<class T, class H = Hash<T>, class E = Equal<T> >
class Set {
public:
// code omitted...
bool contains(const T& element) const {
return s_.find(element) != end();
}
bool insert(const T& element) {
return s_.insert(element).second;
}
// code omitted...
private:
::boost::unordered::unordered_set<T, H, E> s_;
};
So if we have some base class:
class Base {
public:
Base(const ::std::string& string) {
if (string.empty())
throw ::std::invalid_argument("String is empty.");
string_ = string;
}
virtual ~Base() {
}
friend bool operator==(const Base& right, const Base& left) {
return typeid(right) == typeid(left) && right.string_ == left.string_;
}
friend bool operator!=(const Base& right, const Base& left) {
return !(right == left);
}
friend ::std::size_t hash_value(Base const& base) {
::boost::hash<std::string> hash;
return hash(base.string_);
}
friend ::std::size_t hash_value(Base* const& base) {
return hash_value(*base);
}
private:
::std::string string_;
};
And some derived class:
class Derived: public Base {
public:
Derived(const ::std::string& string) :
Base(string) {
}
virtual ~Derived() {
}
};
Then we can even use polymorphism (which was my primary intention BTW):
Derived d1("¯\_(ツ)_/¯");
Derived d2("¯\_(ツ)_/¯");
Set<Base*> set;
set.insert(&d1);
assert(set.contains(&d2));
Hope this helps. Any suggestions are welcome.
I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.
Here is the definition of the class template
// GenericContainer.hpp
// --------------------------------------
class ContainerItem
{
protected:
virtual ContainerItem& getInvalid() = 0;
public:
virtual ~ContainerItem();
bool isValid() const;
};
template<typename D, typename B>
class IsDerivedFrom
{
static void Constraints(D* p)
{
B* pb = p; // this line only works if 'D' inherits 'B'
pb = p; // suppress warnings about unused variables
}
protected:
void IsDerivedFrom2() { void(*p)(D*) = Constraints; }
};
// Force it to fail in the case where B is void
template<typename D>
class IsDerivedFrom<D, void>
{
void IsDerivedFrom2() { char* p = (int*)0; /* error */ }
};
template <class T>
class GenericContainer : public IsDerivedFrom<T, ContainerItem>
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
// Class to be contained (PURPOSELY, does not derive from ContainerItem)
// Data.hpp
//----------------------------------------------------------------
class Data
{ /* implem details */};
// Container for Data items
// Dataset.h
// ----------------------------------------------------------------------------
#include "GenericContainer.hpp"
#include "Data.hpp"
class Dataset: public GenericContainer<Data>
{
public:
Data& getInvalid();
};
// C++ source
// -----------------------------------------------------------
#include "Dataset.hpp"
Dataset ds;
Can anyone explain why the code above compiles?.
[Edit]
The code above should NOT compile for two reasons:
The class 'Data' does NOT derive from ContainerItem, and yet it can be stored in GenericContainer (as illustrated by the class Dataset). Incidentally, this issue has now been resolved thanks to the answer given by Omifarious and jdv
The class 'Data' does NOT implement the pure virtual method declared in the ABC ContainerItem - using the fixes recommended in the answers below, the first issue (enforcement of policy) is resolved, however the compiler fails to notice that Data does not implement the getInvalid() method of the ContainerItem 'interface'. Why is the compiler missing this glaring mistake?
BTW, compiler and OS details are:
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Change IsDerivedFrom2 to IsDerivedFrom and it fails to compile in just the expected manner.
The problem is that a method from a template class is never instantiated if it isn't called. Changing the name makes it a constructor, so it then ends up being called by the constructors of classes derived from IsDerivedFrom. It will still compile to empty code. The compiler will optimize it away the dead assignment.
I would recommend you not write template code like this yourself if you can manage to use Boost, particularly is_base_of from the Boost type traits library.
In particular, your GenericContainer template can be more simply and easily implemented this way using Boost:
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
template <class T>
class GenericContainer
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
GenericContainer() {
BOOST_STATIC_ASSERT( (::boost::is_base_of<ContainerItem, T>::value) );
}
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
The Constraints function is not generated because IsDerivedFrom2 is never referenced. This is required behavior for C++. Maybe it helps to call it from the constructor. Otherwise, check the boost library for functionality like this.
If I want to create a function template, where the template parameter isn't used in the argument list, I can do it thusly:
template<T>
T myFunction()
{
//return some T
}
But the invocation must specify the 'T' to use, as the compiler doesn't know how to work it out.
myFunction<int>();
But, suppose I wanted to do something similar, but for the '[]' operator.
template
T SomeObject::operator [ unsigned int ]
{
//Return some T
}
Is there any way to invoke this operator?
This doesn't appear valid:
SomeObject a;
a<int>[3];
This should work:
class C
{
public:
template <class T>
T operator[](int n)
{
return T();
}
};
void foo()
{
C c;
int x = c.operator[]<int>(0);
}
But it's of no real value because you'd always have to specify the type, and so it looks like a very ugly function call - the point of an operator overload is to look like an operator invocation.
Boost.Program_options uses this neat syntax:
int& i = a["option"].as<int>();
Which is achieved with something like this:
class variable_value
{
public:
variable_value(const boost::any& value) : m_value(value) {}
template<class T>
const T& as() const {
return boost::any_cast<const T&>(m_value);
}
template<class T>
T& as() {
return boost::any_cast<T&>(m_value);
}
private:
boost::any m_value;
};
class variables_map
{
public:
const variable_value& operator[](const std::string& name) const
{
return m_variables[name];
}
variable_value& operator[](const std::string& name)
{
return m_variables[name];
}
private:
std::map<std::string, variable_value> m_variables;
};
You could adapt this idea to suit your own needs.
Like with any operator, the function name is operator#, so:
a.operator[]<int>(3);
You can use a.operator[]<int>(1);
But why do you want this?
This may not be an optimal solution, but you could directly call the operator as such:
a.operator[](3);
I tried this in g++ with the following test:
class MyClass {
public:
template<class T>
T operator[](unsigned int) {
// do something
return T();
}
};
int main(int argc, char* argv[]) {
MyClass test;
test.operator[]<int>(0);
//test<int>[0]; // doesn't compile, as you mentioned
return 0;
}
If you need to define operator[] then probably define the template at the class level. Something like this:
template<class T>
class C
{
public:
T operator[](int n)
{
return T();
}
};
int main()
{
C<int> c;
int x = c[0];
return 0;
}
I have a hard time coming up with an example where this would be needed (couldn't you just overload the operator instead?), but here's my thoughts anyway:
Since you cannot use the infix operator syntax with templatized operators, you might want to do the template instantiation before you call the operator. A proxy might be a way to do this.
class some_class {
private:
template<class T> class proxy {
some_class* that_;
public:
proxy(some_class* that) : that_(that) {}
T& operator[](std::size_type idx) {return that->get<T>(idx);}
};
template<class T> class const_proxy {
some_class* that_;
public:
proxy(const some_class* that) : that_(that) {}
const T& operator[](std::size_type idx) const {return that->get<T>(idx);}
};
template< typename T > proxy<T> get_array() {return proxy<T>(this);}
template< typename T > const_proxy<T> get_array() const {return proxy<T>(this);}
template< typename T > T& get(std::size_t idx) {/* whatever */}
template< typename T > const T& get(std::size_t idx) const {/* whatever */}
};
// This is a lousy use case.
// Did I already say I have a hard time imagining how to use this?
template< typename T >
void f(some_class& some_object, sid::size_t idx)
{
T& = some_object.get_array<T>()[idx];
}