I am attempting to create a template "AutoClass" that create an arbitrary class with an arbitrary set of members, such as:
AutoClass<int,int,double,double> a;
a.set(1,1);
a.set(0,2);
a.set(3,99.7);
std::cout << "Hello world! " << a.get(0) << " " << a.get(1) << " " << a.get(3) << std::endl;
By now I have an AutoClass with a working "set" member:
class nothing {};
template < typename T1 = nothing, typename T2 = nothing, typename T3 = nothing,
typename T4 = nothing, typename T5 = nothing, typename T6 = nothing>
class AutoClass;
template <>
class AutoClass<nothing, nothing, nothing,
nothing, nothing, nothing>
{
public:
template <typename U> void set(int n,U v){}
};
template < typename T1, typename T2, typename T3,
typename T4, typename T5, typename T6>
class AutoClass: AutoClass<T2,T3,T4,T5,T6>
{
public:
T1 V;
template <typename U> void set(int n,U v)
{
if (n <= 0)
V = v;
else
AutoClass<T2,T3,T4,T5,T6>::set(n-1,v);
}
};
and I started to have problems implementing the corresponding "get". This approach doesn't compile:
template < typename T1, typename T2, typename T3,
typename T4, typename T5, typename T6>
class AutoClass: AutoClass<T2,T3,T4,T5,T6>
{
public:
T1 V;
template <typename U> void set(int n,U v)
{
if (n <= 0)
V = v;
else
AutoClass<T2,T3,T4,T5,T6>::set(n-1,v);
}
template <typename W> W get(int n)
{
if (n <= 0)
return V;
else
return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
}
template <> T1 get(int n)
{
if (n <= 0)
return V;
else
return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
}
};
Besides, it seems I need to implement get for the <nothing, nothing, nothing, nothing, nothing, nothing> specialization. Any Idea on how to solve this?
First of all, I prefer Boost.Fusion to Boost.Tuple as it supports a better mixin of template metaprogramming and runtime algorithms I think.
For example, I'd like to present you a little marvel:
struct Name {}; extern const Name name;
struct GivenName {}; extern const GivenName givenName;
struct Age {}; extern const Age age;
class Person
{
public:
template <class T>
struct value
{
typedef typename boost::fusion::result_of::at_key<data_type const,T>::type type;
};
template <class T>
struct has
{
typedef typename boost::fusion::result_of::has_key<data_type,T>::type type;
};
template <class T>
typename value<T>::type
get(T) { return boost::fusion::at_key<T>(mData); }
template <class T>
Person& set(T, typename value<T>::type v)
{
boost::fusion::at_key<T>(mData) = v; return *this;
};
private:
typedef boost::fusion::map <
std::pair<Name, std::string>,
std::pair<GivenName, std::string>,
std::pair<Age, unsigned short>
> data_type;
data_type mData;
};
It's really fun to use:
Person p;
p.set(name, "Rabbit").set(givenName, "Roger").set(age, 22);
Well, I myself prefer indexing by classes than by indices, because I can convey meaning as well as adding type checking ;)
Might I recommend using the Boost library's extensive (and well-tested and cross-platform) set of template-magicky classes? It sounds like what you're looking for is boost::tuple. Any time you can get away with not writing your own code—especially in a complicated situation with templates—you should use someone else's.
As others mentioned, you probably should be able to get where you want by reusing existing implementations from Boost or elsewhere.
If you would be doing something that can't be done using those or if you're curious:
try to keep the pseudo-variadic templates out of the implementation
use type-lists instead to allow for recursive meta-functions etc.
use pseudo-variadic templates as an interface if needed that forwards
to the implementation
do as much at compile-time as possible, especially checks for indices etc.
A simple approach, utilizing MPL for convenience could look something like this:
template<class Types, size_t N> struct holder
// recursively derive from holder types:
: holder<Types, N-1>
{
typename boost::mpl::at_c<Types,N>::type value;
};
// specialization that terminates the recursive derivation:
template<class Types> struct holder<Types,0> {
typename boost::mpl::at_c<Types,0>::type value;
};
template<class Types>
class AutoClass
// recursively derive from holder types:
: holder<Types, boost::mpl::size<Types>::value-1>
{
enum { n = boost::mpl::size<Types>::value };
public:
template<size_t N, class U> void set(const U& u) {
// index check at compile time:
BOOST_STATIC_ASSERT((N < n));
// cast to responsible holder base:
static_cast<holder<Types,N>*>(this)->value = u;
}
template<size_t N> typename boost::mpl::at_c<Types,N>::type get() const {
// index check at compile time:
BOOST_STATIC_ASSERT((N < n));
// cast to responsible holder base:
return static_cast<const holder<Types,N>*>(this)->value;
}
};
Usage:
typedef boost::mpl::vector<int,std::string> Types;
AutoClass<Types> a;
a.set<0>(42);
assert(a.get<0>() == 42);
a.set<1>("abcde");
assert(a.get<1>() == "abcde");
Keep in mind that this can still be wrapped with pseudo-variadic templates for end-user-convenience.
You need to implement for <nothing, nothing...> because of your base case. Consider:
template <typename W> W get(int n)
{
if (n <= 0)
return V;
else
return AutoClass<T2,T3,T4,T5,T6>::get(n-1);
}
Consider what happens when you call this function on a full AutoClass with an n of 5. It creates an autoclass with 5 members and calls with n = 4....and again until it reaches this point:
template <typename W> W get(int n) // current autoclass is <T6,nothing,nothing...>
{
if (n <= 0)
return V;
else
return AutoClass<T2,T3,T4,T5,T6>::get(n-1); // this is <nothing, nothing...>
}
Sure, the call to this autoclass won't happen but the compiler has to compile that code anyway because you've told it to.
You also need to make an AutoClass<nothing,...>::get because n could be 1093.
I don't see a way out of this with your current interface. If you put the n in the template argument you could make a special case that wouldn't do this. In this case you cannot. I think you're going to run into a lot of issues because you've chosen this interface that are going to be rather difficult to solve. For example, what happens when W is 'int' but AutoClass::get(n-1) returns a double or worse, something totally incompatible?
Related
At work, I ran into a situation where the best type to describe the result returned from a function would be std::variant<uint64_t, uint64_t> - of course, this isn't valid C++, because you can't have two variants of the same type. I could represent this as a std::pair<bool, uint64_t>, or where the first element of the pair is an enum, but this is a special case; a std::variant<uint64_t, uint64_t, bool> isn't so neatly representable, and my functional programming background really made me want Either - so I went to try to implement it, using the Visitor pattern as I have been able to do in other languages without native support for sum types:
template <typename A, typename B, typename C>
class EitherVisitor {
virtual C onLeft(const A& left) = 0;
virtual C onRight(const B& right) = 0;
};
template <typename A, typename B>
class Either {
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) = 0;
};
template <typename A, typename B>
class Left: Either<A, B> {
private:
A value;
public:
Left(const A& valueIn): value(valueIn) {}
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) {
return visitor.onLeft(value);
}
};
template <typename A, typename B>
class Right: Either<A, B> {
private:
B value;
public:
Right(const B& valueIn): value(valueIn) {}
template <typename C>
virtual C Accept(EitherVisitor<A, B, C> visitor) {
return visitor.onRight(value);
}
};
C++ rejects this, because the template method Accept cannot be virtual. Is there a workaround to this limitation, that would allow me to correctly represent the fundamental sum type in terms of its f-algebra and catamorphism?
Perhaps the simplest solution is a lightweight wrapper around T for Right and Left?
Basically a strong type alias (could also use Boost's strong typedef)
template<class T>
struct Left
{
T val;
};
template<class T>
struct Right
{
T val;
};
And then we can distinguish between them for visitation:
template<class T, class U>
using Either = std::variant<Left<T>, Right<U>>;
Either<int, int> TrySomething()
{
if (rand() % 2 == 0) // get off my case about rand(), I know it's bad
return Left<int>{0};
else
return Right<int>{0};
}
struct visitor
{
template<class T>
void operator()(const Left<T>& val_wrapper)
{
std::cout << "Success! Value is: " << val_wrapper.val << std::endl;
}
template<class T>
void operator()(const Right<T>& val_wrapper)
{
std::cout << "Failure! Value is: " << val_wrapper.val << std::endl;
}
};
int main()
{
visitor v;
for (size_t i = 0; i < 10; ++i)
{
auto res = TrySomething();
std::visit(v, res);
}
}
Demo
std::variant<X,X> is valid C++.
It is a bit awkward to use, because std::visit doesn't give you the index, and std::get<X> won't work either.
The way you can work around this is to create a variant-of-indexes, which is like a strong enum.
template<std::size_t i>
using index_t = std::integral_constant<std::size_t, i>;
template<std::size_t i>
constexpr index_t<i> index = {};
template<std::size_t...Is>
using number = std::variant< index_t<Is>... >;
namespace helpers {
template<class X>
struct number_helper;
template<std::size_t...Is>
struct number_helper<std::index_sequence<Is...>> {
using type=number<Is...>;
};
}
template<std::size_t N>
using alternative = typename helpers::number_helper<std::make_index_sequence<N>>::type;
we can then extract the alternative from a variant:
namespace helpers {
template<class...Ts, std::size_t...Is, class R=alternative<sizeof...(Ts)>>
constexpr R get_alternative( std::variant<Ts...> const& v, std::index_sequence<Is...> ) {
constexpr R retvals[] = {
R(index<Is>)...
};
return retvals[v.index()];
}
}
template<class...Ts>
constexpr alternative<sizeof...(Ts)> get_alternative( std::variant<Ts...> const& v )
{
return helpers::get_alternative(v, std::make_index_sequence<sizeof...(Ts)>{});
}
so now you have a std::variant<int, int>, you can
auto which = get_alternative( var );
and which is a variant, represented at runtime by an integer which is the index of the active type in var. You can:
std::variant<int, int> var( std::in_place_index_t<1>{}, 7 );
auto which = get_alternative( var );
std::visit( [&var](auto I) {
std::cout << std::get<I>(var) << "\n";
}, get_alternative(var) );
and get access to which of the alternative possibilities in var is active with a compile time constant.
The get_alternative(variant), I find, makes variant<X,X,X> much more usable, and fills in the hole I think you might be running into.
Live example.
Now if you don't need a compile-time index of which one is active, you can just call var.index(), and visit via visit( lambda, var ).
When you construct the variant, you do need the compile time index to do a variant<int, int> var( std::in_place_index_t<0>{}, 7 ). The wording is a bit awkward, because while C++ supports variants of multiples of the same type, it considers them a bit less likely than a "standard" disjoint variant outside of generic code.
But I've used this alternative and get_alternative like code to support functional programming like data glue code before.
First off my use case, as I may think in the wrong direction: I want to create a map that maps a value to types. So for example:
Map<std::string> map;
map.insert<int, double, char>("Hey");
auto string = map.at<int, double, char>();
This alone is fairly easy to do with std::type_index. However, I want to add the possibility to match types that are not exact the searched ones, when they are convertible. So the following should also return "Hey", as float can be converted to double:
auto string = map.at<int, float, char>();
I can't use type_index for this case as std::is_convertible only works directly on types. This would be the version without conversion, but as far as it seems it's not easily possible to add conversion handling into it without major changes.
My current attempt looks kind of like the following, please note that this is not working and just shows what I have tried to implement:
template<typename T>
class Map {
T value;
std::vector<Map<T>> children; // all the children of the current node.
// in the above example, if this was
// the int node, the only child
// would be the double node
template<typename T1>
constexpr bool is_convertible() const {
return std::is_convertible<__T__, T1>::value; // this isn't applicable
// since __T__ can't be
// stored (this nodes
// type)
}
public:
template<typename T1, typename... Tn>
void insert(T&& value) {
// iterate through/create the child nodes until the last template param
}
template<typename T1, typename... Tn>
T& at() {
// iterate through thechild nodes until a matching child is found
// either exact match or a convertible
for(auto &c: children) {
// if the above function would work
if(c.template is_convertible<T1>()) {
return c.template at<Tn...>();
}
}
}
}
Now I'm at my wits end how to achieve this. I thought of implementing lambdas as comparator functions, but while the lambda can store the type of the current node, it can't accept a template parameter on call to compare to.
Is there some C+1y generic lambda comparator magic, or even an easier way?
I hope this does what you want, there's ample space for extension and for creating template specialization that attach to any type combination you want. It's not super-pretty, but it can probably be refactored a bit and beautified.
#include <iostream>
template <typename... Args>
struct map {
};
template <>
struct map<int, float, char> {
static constexpr char value[] = "int float char";
};
constexpr char map<int,float,char>::value[];
template <typename T>
struct map<int, T> {
static constexpr typename std::enable_if<std::is_integral<T>::value, char>::type value[] = "int, T";
};
template <typename T>
constexpr typename std::enable_if<std::is_integral<T>::value, char>::type map<int,T>::value[];
int main() {
std::string v = map<int,float,char>::value;
std::string w = map<int,int>::value;
std::string w2 = map<int,unsigned>::value;
// std::string w3 = map<int,float>::value; Won't compile
std::cout << v << "\n";
std::cout << w << "\n";
std::cout << w2 << "\n";
return 0;
}
I wrote some weird code using boost::fusion that comes close to doing what you want:
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/include/insert.hpp>
#include <boost/fusion/include/pair.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <string>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <memory>
template <std::size_t Value1, std::size_t Value2>
struct MinSizeT {
static const std::size_t value = (Value1 > Value2) ? Value2 : Value1;
};
template<typename T1, typename T2, std::size_t N>
struct TupleIsConvertibleHelper {
static const bool value = std::is_convertible<typename std::tuple_element<N - 1, T1>::type, typename std::tuple_element<N - 1, T2>::type>::value && TupleIsConvertibleHelper<T1, T2, N - 1>::value;
};
template<typename T1, typename T2>
struct TupleIsConvertibleHelper<T1, T2, 0> {
static const bool value = true;
};
template<typename T1, typename T2>
bool TupleIsConvertible() { // Return true if all types in T1 are convertible to their corresponding type in T2
if (std::tuple_size<T1>::value != std::tuple_size<T2>::value)
return false;
constexpr std::size_t minSize = MinSizeT<std::tuple_size<T1>::value, std::tuple_size<T2>::value>::value;
return TupleIsConvertibleHelper<T1, T2, minSize>::value;
}
template<typename MapInserter>
class Map {
MapInserter mc;
template<typename... Types>
struct do_at {
template <typename T>
void operator()(T const& x) const { // Find an exact match or the last convertible match
typedef std::tuple<Types...> t1;
typedef typename T::first_type t2;
if (exactMatch)
return;
if (std::is_same<t1, t2>::value) {
exactMatch = true;
value = x.second;
}
else if (TupleIsConvertible<t1, t2>())
value = x.second;
}
mutable bool exactMatch;
mutable typename MapInserter::value_type value;
do_at() : exactMatch(false) {}
};
public:
Map(MapInserter _mc) : mc(_mc) { }
template<typename... Types>
typename MapInserter::value_type at() {
do_at<Types...> res;
boost::fusion::for_each(mc.data->map, res);
return res.value;
}
};
template<typename ValueType, typename MapType = boost::fusion::map<>, typename ParentType = void*>
struct MapInserter {
typedef ValueType value_type;
struct Helper {
MapType map;
std::shared_ptr<ParentType> parent; // Must keep parent alive because fusion is lazy.
Helper() = default;
Helper(MapType&& _map, std::shared_ptr<ParentType> _parent) : map(std::move(_map)), parent(_parent) {}
};
std::shared_ptr<Helper> data;
template<typename... KeyTypes>
auto Insert(ValueType value) -> MapInserter<ValueType, decltype(boost::fusion::insert(data->map, boost::fusion::end(data->map), boost::fusion::make_pair<std::tuple<KeyTypes...>>(value))), Helper> {
auto newMap = boost::fusion::insert(data->map, boost::fusion::end(data->map), boost::fusion::make_pair<std::tuple<KeyTypes...>>(value));
return MapInserter<ValueType, decltype(newMap), Helper>(std::move(newMap), data);
}
MapInserter() : data(std::make_shared<Helper>()) { }
MapInserter(MapType&& _map, std::shared_ptr<ParentType> _parent) : data(std::make_shared<Helper>(std::move(_map), _parent)) {}
MapInserter(MapInserter&&) = default;
MapInserter(const MapInserter&) = default;
};
int main() {
auto mc = MapInserter<std::string>().
Insert<int, char, float>("***int, char, float***").
Insert<float, double>("***float, double***").
Insert<int>("***int***").
Insert<unsigned, bool>("***unsigned, bool***");
Map<decltype(mc)> map(mc);
std::cout << map.at<int, char, float>() << std::endl; // "***int, char, float***"
std::cout << map.at<int, char, double>() << std::endl; // "***int, char, float***"
std::cout << map.at<char>() << std::endl; // "***int***"
return 0;
}
template<class...>struct types { typedef types type; };
template<class T, class types>struct type_index;
template<class T, class...Ts>
struct type_index<T,types<T, Ts...>>:
std::integral_constant<unsigned,0>
{};
template<class T, class T0, class...Ts>
struct type_index<T,types<T0, Ts...>>:
std::integral_constant<unsigned,type_index<T,types<Ts...>::value+1>
{};
template<template<class>class filter, class types_in, class types_out=types<>, class details=void>
struct filter;
template<template<class>class filter, class T0, class... Ts, class... Zs>
struct filter<filter, types<T0,types...>, types<Zs...>,
typename std::enable_if< filter<T0>::value >::type
>: filter<filter, types<types...>, types<Zs...,T0>>
{};
template<template<class>class filter, class T0, class... Ts, class... Zs>
struct filter<filter, types<T0,types...>, types<Zs...>,
typename std::enable_if< !filter<T0>::value >::type
>: filter<filter, types<types...>, types<Zs...>>
{};
template<template<class>class filter, class... Zs>
struct filter<filter, types<>, types<Zs...>,
void
>: types<Zs...>
{};
template<typename T>
struct convertable_to_test {
template<typename U>
using test = std::is_convertible<U, T>;
};
template<class T, class types>
struct get_convertable_to_types:filter< convertable_to_test<T>::template test, types> {};
which is a start.
Create a master types<Ts...> of all of the types your system supports. Call this SupportedTypes.
Map types<Ts...> to std::vector<unsigned> of each type offset in the above list. Now you can store a collection of types at runtime. Call this a runtime type vector.
When adding an entry types<Args...> to the map, run get_convertable_to_types on each type in types<Args...>, and build a cross product in types< types<...>... >. Store the resulting exponential number of runtime type vectors in your implementation details map.
When you query with types<Ts...>, conver to the runtime type vector, and look it up in the implementation details map. And done!
An alternative approach would be to write get_convertable_from_types, and do the mapping to an exponential number of types<Ts...> at the query point, convert each to a runtime type vector. When adding stuff to the map, store only one runtime type vector. This has slower lookup performance, but faster setup performance, and uses far less memory.
I was going to finish this, but got busy.
Is it possible to iterate over all elements in a struct or class?
For example if I have a struct of three elements of different type:
struct A {
classA a;
classB b;
classC c;
};
then I need some iterator such that a method next() would give me the value
of the next element. The problem is that as you see, the values have different types.
Nope, not with the language as it is.
You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.
Alternatively put the items in a std::vector and use that to provide the iteration.
No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).
Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.
Then you will need a construct "iterating" over the tuple, such as:
void applyToAll() { }
template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
std::forward<Lambda>(closure)();
applyToAll(std::forward<Lambdas>(closures)...);
}
// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
typedef _Sequence<Is...> type;
};
template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };
template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };
template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
applyToAll(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
_foreachElemInTuple(
_MakeSequence<std::tuple_size<
typename std::decay<Tuple>::type>::value>(),
std::forward<Tuple>(t), std::forward<Functor>(f)
);
}
Then you can call foreachElemInTuple(yourTuple, some_adapter()).
Your adapter will look like:
struct some_adapter {
template <typename... Args>
// A little bit of C++14, you can also just -> decltype the thing
decltype(auto) operator()(Args&& ... args) const {
return doStuff(std::forward<Args>(args)...);
}
};
As everyone else says, you cannot directly iterate over data members of a
class. However, it is not difficult to do it indirectly, provided of course that
you can access each of the data members you want to iterate over. The idea
in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple
of references to those data members.
The following program shows how to obtain such a tuple, using std::forward_as_tuple,
and another way to do the iterating by compiletime recursion, without
auxiliary apparatus.
#include <tuple>
/* You want to be able do something with the values of the members of an `A`
in turn.
*/
struct A
{
char ch;
int i;
double d;
// May also have members of class type. It doesn't matter
};
/* 1) Provide yourself with the means of creating a sequence that contains
references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
return std::forward_as_tuple(a.ch,a.i,a.d);
}
/* 2) Provide yourself with a means of applying some operation, `Func`,
to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
for_each_in_tuple(get_A_vals(a),func);
}
// Testing...
#include <iostream>
// A specimen operation: just prints its argument
struct printer
{
template<typename T>
void operator () (T && t)
{
std::cout << t << std::endl;
}
};
int main()
{
A a{'a',1,2.0};
for_each_in_A(a,printer());
return 0;
}
// EOF
The program outputs:
a
1
2
If you have control of the structs or classes over whose members you need to
iterate, you may consider whether it is practical simply to dispense with them
and use the corresponding std::tuples everywhere.
Code built with gcc 4.8.2 and clang 3.3, -std=c++11.
Problem statement (for an educational purpose):
-Implement method printContainer which works for STL containers vector, stack, queue and deque.
I made a solution, but I don`t like it due to excessive amount of code.
What I did to solve the problem:
1. Designed generic function which expects uniform interface from containers for operations: get value of last element and erase that element from the container
template <typename T>
void printContainer(T container)
{
cout << " * * * * * * * * * * " << endl;
cout << " operator printContainer(T container). Stack, queue, priority queue"
<< endl;
cout << typeid(container).name() << endl;
while (!container.empty())
{
cout << top(container) << " ";
pop(container);
}
cout << endl;
cout << " * * * * * * * * * * * " << endl;
}
For each container I implemented functions that allows to provide uniform interface
(I want to refactor the following code snippet):
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
template <typename T, typename Base>
typename stack<T, Base>::value_type top(const stack<T, Base>& s)
{
return s.top();
}
template <typename T, typename Base>
typename queue<T, Base>::value_type top(const queue<T, Base>& q)
{
return q.front();
}
template <typename T, typename Base>
typename priority_queue<T, Base>::value_type top(const priority_queue<T,
Base>& pq)
{
return pq.top();
}
template <typename T>
void pop(vector<T>& v)
{
return v.pop_back();
}
template <typename T, typename Base>
void pop(stack<T, Base>& s)
{
return s.pop();
}
template <typename T, typename Base>
void pop(queue<T, Base>& q)
{
return q.pop();
}
template <typename T, typename Base>
void pop(priority_queue<T,Base>& pq)
{
return pq.pop();
}
I wan`t to replace it with something like this:
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
return c.back();
if (typeid(container).name == typeid(queue<T,Base>))
return c.front();
else
return c.top();
}
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type pop(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
c.pop_back();
else
return c.pop();
}
but it doesn`t work, I get errors like :
Error 1 error C2784: 'container<T,Base>::value_type top(container<T,Base> &)' : could not deduce template argument for 'container<T,Base> &' from 'std::stack<_Ty>'
Question:
that adjacements should I made in template template paramter to sort out errors, maybe there is something that I overlooked or exist logical errors.
Any way, any usefull information is welcomed.
Thanks in advance!
UPDATE:
// that is how I am trying to invoke the function
int arr[] = {1,2,3,4,5,6,7,8,9,0};
stack<int> s(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));;
queue<int> q(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));
priority_queue<int> pq(arr, arr + sizeof(arr) / sizeof(arr[0]));
printContainer(s);
printContainer(q);
printContainer(pq);
This solution:
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
return c.back();
if (typeid(container).name == typeid(queue<T,Base>))
return c.front();
else
return c.top();
}
Won't work, because if() realizes a run-time selection, which means that the code of all branches must compile, even though exactly only one of them evaluates to true, and function top() is not provided by all containers (e.g. vector).
Consider this simpler example for an explanation:
struct X { void foo() { } };
struct Y { void bar() { } };
template<bool b, typename T>
void f(T t)
{
if (b)
{
t.foo();
}
else
{
t.bar();
}
}
int main()
{
X x;
f<true>(x); // ERROR! bar() is not a member function of X
Y y;
f<false>(y); // ERROR! foo() is not a member function of Y
}
Here, I am passing a boolean template argument, which is known at compile-time, to function f(). I am passing true if the input is of type X, and therefore supports a member function called foo(); and I am passing false if the input is of type Y, and therefore supports a member function called bar().
Even though the selection works on a boolean value which is known at compile-time, the statement itself is executed at run-time. The compiler will first have to compile the whole function, including the false branch of the if statement.
What you are looking for is some kind of static if construct, which is unfortunately not available in C++.
The traditional solution here is based on overloading, and looks in fact like the one you provided originally.
I'd come at it the other way around. I'd write a generic function that uses iterators:
template <class Iter>
void show_contents(Iter first, Iter last) {
// whatever
}
then a generic function that takes containers:
template <class Container>
void show_container(const Container& c) {
show_contents(c.begin(), c.end());
}
then a hack to get at the container that underlies a queue or a stack:
template <class C>
struct hack : public C {
hack(const C& cc) : C(cc) { }
typename C::Container::const_iterator begin() const {
return this->c.begin();
}
typename C::Container::const_iterator end() const {
return this->c.end();
}
};
then define specializations to create these objects and show their contents:
template <class T>
void show_container(const stack<T>& s) {
hack<stack<T>> hack(s);
show_contents(hack.begin(), hack.end());
}
template <class T>
void show_container(const queue<T>& q) {
hack<stack<T>> hack(q);
show_contents(hack.begin(), hack.end());
}
While Andy's answer is already a good one, I'd like to add one improvement about your implementation. You could improve it to support more container specializations, as your overloads don't allow all the template parameters that the STL containers have to be non-default. For example, look at your code:
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
and now compare it with the definition of std::vector. The class template has two parameters, namely std::vector<T,Allocator=std::allocator<T>>. You overload only accepts those std::vectors where the second parameter is std::allocator<T>.
While you could manually add more parameters to your code, there is a better alternative: Variadic templates. You can use the following code for a truly generic version for all std::vectors:
template <typename... Ts>
typename vector<Ts...>::value_type top(const vector<Ts...>& v)
{
return v.back();
}
and, of course, you can use the same technique for all other containers and don't need to worry about the exact number of template parameters they have. Some containers even have up to five template parameters, so this can be quite annoying if you don't use variadic templates.
One caveat: Some older compilers might not like the variadic version, you'll have to manually iterate all parameters.
Is there a way, presumably using templates, macros or a combination of the two, that I can generically apply a function to different classes of objects but have them respond in different ways if they do not have a specific function?
I specifically want to apply a function which will output the size of the object (i.e. the number of objects in a collection) if the object has that function but will output a simple replacement (such as "N/A") if the object doesn't. I.e.
NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------> 10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"
I expect that this might be something similar to a static assertion although I'd clearly want to compile a different code path rather than fail at build stage.
From what I understand, you want to have a generic test to see if a class has a certain member function. This can be accomplished in C++ using SFINAE. In C++11 it's pretty simple, since you can use decltype:
template <typename T>
struct has_size {
private:
template <typename U>
static decltype(std::declval<U>().size(), void(), std::true_type()) test(int);
template <typename>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) type;
enum { value = type::value };
};
If you use C++03 it is a bit harder due to the lack of decltype, so you have to abuse sizeof instead:
template <typename T>
struct has_size {
private:
struct yes { int x; };
struct no {yes x[4]; };
template <typename U>
static typename boost::enable_if_c<sizeof(static_cast<U*>(0)->size(), void(), int()) == sizeof(int), yes>::type test(int);
template <typename>
static no test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};
Of course this uses Boost.Enable_If, which might be an unwanted (and unnecessary) dependency. However writing enable_if yourself is dead simple:
template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };
In both cases the method signature test<U>(int) is only visible, if U has a size method, since otherwise evaluating either the decltype or the sizeof (depending on which version you use) will fail, which will then remove the method from consideration (due to SFINAE. The lengthy expressions std::declval<U>().size(), void(), std::true_type() is an abuse of C++ comma operator, which will return the last expression from the comma-separated list, so this makes sure the type is known as std::true_type for the C++11 variant (and the sizeof evaluates int for the C++03 variant). The void() in the middle is only there to make sure there are no strange overloads of the comma operator interfering with the evaluation.
Of course this will return true if T has a size method which is callable without arguments, but gives no guarantees about the return value. I assume wou probably want to detect only those methods which don't return void. This can be easily accomplished with a slight modification of the test(int) method:
// C++11
template <typename U>
static typename std::enable_if<!is_void<decltype(std::declval<U>().size())>::value, std::true_type>::type test(int);
//C++03
template <typename U>
static typename std::enable_if<boost::enable_if_c<sizeof(static_cast<U*>(0)->size()) != sizeof(void()), yes>::type test(int);
There was a discussion about the abilities of constexpr some times ago. It's time to use it I think :)
It is easy to design a trait with constexpr and decltype:
template <typename T>
constexpr decltype(std::declval<T>().size(), true) has_size(int) { return true; }
template <typename T>
constexpr bool has_size(...) { return false; }
So easy in fact that the trait loses most of its value:
#include <iostream>
#include <vector>
template <typename T>
auto print_size(T const& t) -> decltype(t.size(), void()) {
std::cout << t.size() << "\n";
}
void print_size(...) { std::cout << "N/A\n"; }
int main() {
print_size(std::vector<int>{1, 2, 3});
print_size(1);
}
In action:
3
N/A
This can be done using a technique called SFINAE. In your specific case you could implement that using Boost.Concept Check. You'd have to write your own concept for checking for a size-method. Alternatively you could use an existing concept such as Container, which, among others, requires a size-method.
You can do something like
template< typename T>
int getSize(const T& t)
{
return -1;
}
template< typename T>
int getSize( const std::vector<T>& t)
{
return t.size();
}
template< typename T , typename U>
int getSize( const std::map<T,U>& t)
{
return t.size();
}
//Implement this interface for
//other objects
class ISupportsGetSize
{
public:
virtual int size() const= 0;
};
int getSize( const ISupportsGetSize & t )
{
return t.size();
}
int main()
{
int s = getSize( 4 );
std::vector<int> v;
s = getSize( v );
return 0;
}
basically the most generic implementation is always return -1 or "NA" but for vector and maps it will return the size. As the most general one always matches there is never a build time failure
Here you go. Replace std::cout with the output of your liking.
template <typename T>
class has_size
{
template <typename C> static char test( typeof(&C::size) ) ;
template <typename C> static long test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
template<bool T>
struct outputter
{
template< typename C >
static void output( const C& object )
{
std::cout << object.size();
}
};
template<>
struct outputter<false>
{
template< typename C >
static void output( const C& )
{
std::cout << "N/A";
}
};
template<typename T>
void NO_OF_ELEMENTS( const T &object )
{
outputter< has_size<T>::value >::output( object );
}
You could try something like:
#include <iostream>
#include <vector>
template<typename T>
struct has_size
{
typedef char one;
typedef struct { char a[2]; } two;
template<typename Sig>
struct select
{
};
template<typename U>
static one check (U*, select<char (&)[((&U::size)!=0)]>* const = 0);
static two check (...);
static bool const value = sizeof (one) == sizeof (check (static_cast<T*> (0)));
};
struct A{ };
int main ( )
{
std::cout << has_size<int>::value << "\n";
std::cout << has_size<A>::value << "\n";
std::cout << has_size<std::vector<int>>::value << "\n";
}
but you have to be careful, this does neither work when size is overloaded, nor when it is a template. When you can use C++11, you can replace the above sizeof trick by decltype magic