I have a large and complex program that makes use of templates quite extensively.
For the sake of simplicity, consider I have the following classes in my program:
BronzeFork , SilverFork, GoldFork
SimpleKnife , SerratedKnife, ButterKnife
RedSpoon , GreenSpoon, BlueSpoon
and then in main.cpp I call a function:
Eating<BronzeFork, SerratedKnife, BlueSpoon>::runEat();
My aim is to be able to be able to feed arguments at runtime to my (compiled) program in order to modify the kind of fork or spoon that I want in this run.
I could do it with a very large switch statement running every combination of forks, knives and spoons but it seems quite messy, convoluted and hard to maintain.
Especially because I am continuously adding kinds of Forks, Knifes and Spoons into the program.
What would be the best way to do this in a clean, efficient way?
This is not necessarily the best way to solve your problem. This is one way to generate a lot of instantiations in a compact manner. It may or may not suit your need.
I assume each of your classes have some kind of ID for run-time selection, say a string. So BronzeFork::id would be "BronzeFork". I will use abbreviated names henceforth.
#include <tuple>
#include <string>
#include <functional>
#include <map>
struct F1 { static constexpr const char* id = "F1"; };
struct F2 { static constexpr const char* id = "F2"; };
struct F3 { static constexpr const char* id = "F3"; };
struct K1 { static constexpr const char* id = "K1"; };
struct K2 { static constexpr const char* id = "K2"; };
struct K3 { static constexpr const char* id = "K3"; };
struct S1 { static constexpr const char* id = "K1"; };
struct S2 { static constexpr const char* id = "K2"; };
struct S3 { static constexpr const char* id = "K3"; };
template <typename F, typename K, typename S> struct Eater
{
static void eat();
};
using t3 = std::tuple<std::string, std::string, std::string>;
std::map<t3, std::function<void()>> funcMap;
Now we need to populate funcMap. We need nested compile-time loops. I implement them as separate templates using fold-expressions. There might be a better way, but this one is very unsophisticated and straightforward so it's easy to understand.
template <typename F, typename K, typename S> struct populate0
{
void operator()() { funcMap.insert({{F::id, K::id, S::id}, Eater<F, K, S>::eat}); }
};
// Inner loop
template <typename Fs, typename K, typename S> struct populate1;
template <typename ... Fs, typename K, typename S>
struct populate1 <std::tuple<Fs...>, K, S> // loop over Fs
{
void operator()() { (populate0<Fs, K, S>()(), ...); }
};
// Middle loop
template <typename Fs, typename Ks, typename S> struct populate2;
template <typename Fs, typename ... Ks, typename S>
struct populate2 <Fs, std::tuple<Ks...>, S> // loop over Ks
{
void operator()() { (populate1<Fs, Ks, S>()(), ...); }
};
// Outer loop
template <typename Fs, typename Ks, typename Ss> struct populate3;
template <typename Fs, typename Ks, typename... Ss> // loop over Ss
struct populate3 <Fs, Ks, std::tuple<Ss...>>
{
void operator()() { (populate2<Fs, Ks, Ss>()(), ...); }
};
Now we need to just run the loop.
populate3<std::tuple<F1, F2, F3>,
std::tuple<K1, K2, K3>,
std::tuple<S1, S2, S3>>()();
Whenever you add another class, add it to this call.
C++ is a compilable language, meaning that the types must be known at code-generation-time. Virtual classes could be a solution to your case.
This is not incompatible with the use of templates, which can keep the source code compact. But all specializations with the types you can meet at run-time must be instantiated.
Related
The function 'Process' is taking a variable number of arguments of variable type. To handle different cases, I have successfully overloaded it like this:
// general case
template <typename ...Types>
void Process( const Types&... items )
// single T
template <typename T>
void Process( const T& t )
// one or more of type NVP<>
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps )
What I want to do - but can't - is the following: I need an overload for cases with any number of leading arguments of a types ATT<> followed by any number of NVP<> like this:
// any number of leading Types ATT<> followed by any number of NVP<>
template <typename ...ATypes, typename ...BTypes>
void Process( const ATT<ATypes>&... atts, const NVP<BTypes>&... nvps )
At first you would think it should be 'easy' for a compiler to match this, if it can already do the other cases. There should be absolutely no ambiguity here!? However, the matching fails, no error messages, but the desired overload it is just ignored by the compiler.
Currently using VS2017 with /std:c++17
Notes:
1. It can, obviously, be done for one leading type ATT<T1> like this
// one leading Type ATT<T1>
template <typename T1, typename ...Types>
void Process( const ATT<T1>& a1, const Types&... remaining )
But for more than one, I need to do some ugly manual recursion. I really want to have the whole pack of leading ATT<...>.
2. I am aware that a leading parameter pack - of general types - always is ambiguous for matching, but for a specialization like ATT<ATypes>... no ambiguity should exist.
You could dispatch from the const Types&... overload based on if Types... matches ATT<T>..., NVP<U>....
The basic strategy here is finding the index of the last ATT<T>, forwarding everything as a tuple, then indexing with the appropriate index sequence to forward to another function where the ATT values and NVP values are in two tuples:
namespace detail {
template<class...>
struct get_split_index;
template<class T, class... Others>
struct get_split_index<T, Others...> {
static constexpr std::size_t i = -1;
};
template<class T, class... Others>
struct get_split_index<ATT<T>, Others...> {
static constexpr std::size_t next = get_split_index<Others...>::i;
static constexpr std::size_t i = next == -1 ? -1 : next + 1u;
};
template<class T, class... Others>
struct get_split_index<NVP<T>, Others...> {
// will be 0 if the rest are all NVP<T>, otherwise -1
static constexpr std::size_t i = get_split_index<Others...>::i;
};
template<>
struct get_split_index<> {
static constexpr std::size_t i = 0;
};
template<typename... ATypes, typename... BTypes, std::size_t... ATT_I, std::size_t... NVP_I>
void Process(const std::tuple<const ATT<ATypes>&...>& att, const std::tuple<const NVP<BTypes>&...>& nvp, std::index_sequence<ATT_I...>, std::index_sequence<NVP_I...>) {
// Use (std::get<ATT_I>(att)) and (std::get<NVP_I>(nvp))
// instead of (atts) and (nvps) that you would use in your
// supposed `void Process(const ATT<ATypes>&..., const NVP<BTypes>&...)`
}
template<typename... Types, std::size_t... ATT_I, std::size_t... NVP_I>
void ProcessDispatch(const std::tuple<Types...>& t, std::index_sequence<ATT_I...> att_i, std::index_sequence<NVP_I...> nvp_i) {
detail::Process(std::forward_as_tuple(std::get<ATT_I>(t)...), std::forward_as_tuple(std::get<NVP_I + sizeof...(ATT_I)>(t)...), att_i, nvp_i);
}
}
template <typename ...Types>
void Process( const Types&... items ) {
constexpr std::size_t split_index = detail::get_split_index<Types...>::i;
if constexpr (split_index != -1) {
// Might want to check `&& sizeof...(Types) != 0`
detail::ProcessDispatch(std::forward_as_tuple(items...), std::make_index_sequence<split_index>{}, std::make_index_sequence<sizeof...(Types) - split_index>{});
} else {
// general case
}
}
template <typename T>
void Process( const T& t ) {
// single T
}
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps ) {
// one or more of type NVP<>
// This can also be folded into `detail::Process`, checking
// `if constexpr (sizeof...(BTypes) == 0)`.
}
Believe you can use a struct to help you here. The compiler can't determine where one parameter pack stops and the other begins, consider:
foo(1, 2.0, '3', "45", 6.0f). The first parameter pack could be nothing, the first, all of them or none of the above. There is no particular reason to prefer one over another. So you can't make a function that accepts two variadics. What you can do, is to split it into two structs, and specify explicitly the arguments for the outer class.
template<typename... Args>
struct S
{
template<typename... Inner>
static void Process(const ATT<Args>&... atts, const NVP<Inner>&... nvps) {}
};
Example for usage:
ATT<double> a1;
ATT<long> a2;
NVP<int> n1;
NVP<const char*> n2;
S<double, long>::Process(a1, a2, n1, n2);
Another version could be by using the constructor. Here, you also get auto-deduction which is easier. Unfortunately, it only works from C++17 and above.
template<typename... Args>
struct S
{
std::tuple<ATT<Args>...> tup;
S(const ATT<Args>&... atts)
: tup(atts...)
{}
template<typename... Inner>
void Process(const NVP<Inner>&... nvps){}
};
template<typename... Args>
S(const ATT<Args>&... atts)->S<Args...>;
And the usage is:
S(ATT(1), ATT(3.4)).Process(NVP("asdf"), NVP(3.4), NVP('f'));
return 0;
Assuming you're OK with getting them as tuples I made this after drawing from https://stackoverflow.com/a/12782697/1480324 :
#include <iostream>
#include <tuple>
template<typename T>
struct ATT {};
template<typename T>
struct NVP {};
template<typename... ATTs, typename... NVPs>
void Process(const std::tuple<ATT<ATTs>...>& atts, const std::tuple<NVP<NVPs>...>& nvps) {
std::cout << sizeof...(ATTs) << std::endl;
std::cout << sizeof...(NVPs) << std::endl;
}
int main() {
Process(std::make_tuple(ATT<int>(), ATT<double>()), std::make_tuple(NVP<std::string>(), NVP<bool>()));
return 0;
}
It compiles on https://www.onlinegdb.com/online_c++_compiler , but I can't test in visual studio.
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.
Suppose I have a std::tuple made up of types like
struct A {
static void tip();
};
struct B {
static void tip();
};
struct Z {
};
std::tuple<const A&,const B&,const Z&> tpl;
Yes, I need separate A, B. (The implementation of ::tip() differs for each type.) What I try to implement is a type-sensitive "visitor" that iterates through the tuple starting from the beginning to the end. Upon visiting a particular element of type T a function should be called depending on whether T has the ::tip() method or not. In the simple example of above only A and B have ::tip() implemented and Z not. So, the iterator should call twice the function for types with the ::tip() method and once the other function.
Here is what I came up with:
template< int N , bool end >
struct TupleIter
{
template< typename T , typename... Ts >
typename std::enable_if< std::is_function< typename T::tip >::value , void >::type
static Iter( const T& dummy , const std::tuple<Ts...>& tpl ) {
std::cout << "tip\n";
std::get<N>(tpl); // do the work
TupleIter<N+1,sizeof...(Ts) == N+1>::Iter( std::get<N+1>(tpl) , tpl );
}
template< typename T , typename... Ts >
typename std::enable_if< ! std::is_function< typename T::tip >::value , void >::type
static Iter( const T& dummy , const std::tuple<Ts...>& tpl ) {
std::cout << "no tip\n";
std::get<N>(tpl); // do the work
TupleIter<N+1,sizeof...(Ts) == N+1>::Iter( std::get<N+1>(tpl) , tpl );
}
};
template< int N >
struct TupleIter<N,true>
{
template< typename T , typename... Ts >
static void Iter( const std::tuple<Ts...>& tpl ) {
std::cout << "end\n";
}
};
I use a dummy instance of the type of the element at the iterator position and decide via enable_if which function to call. Unfortunately this doesn't work/isn't a nice solution:
The compiler complains about recursive instantiation
The const T& dummy is not a clean solution
I was wondering if enable_if is the right strategy to do the decision and how can one recursively iterate through the std::tuple capturing the first type and keeping all the remaining arguments in vital state. Read through How to split a tuple? but it doesn't do any decision.
How can one implement such a thing in a correct and portable way in C++11?
Well, it was harder than I expected, but this works.
Some things you were doing wrong/that I modified:
You can't evaluate this: std::is_function< typename T::tip >::value, since T::tip is not a type. Even if this could be evaluated, what would happen when T::tip does not exist? Substitution would still fail.
Since you use const references as your tuple's inner types, you had to clean them before trying to find the tip member inside them. By cleaning I mean removing const and removing the reference.
That dummy type stuff was not a good idea, there was no need to use that parameter. You can achieve the same thing using std::tuple_element, which retrieves the i-th type from a tuple.
I modified TupleIter's template parameters to the following, which means:
"TupleIter that processes the index-th type, inside a tuple of size n".
template<size_t index, size_t n>
struct TupleIter;
The whole code is this:
#include <tuple>
#include <iostream>
#include <type_traits>
struct A {
static void tip();
};
struct B {
static void tip();
};
struct Z {
};
// Indicates whether the template parameter contains a static member named tip.
template<class T>
struct has_tip {
template<class U>
static char test(decltype(&U::tip));
template<class U>
static float test(...);
static const bool value = sizeof(test<typename std::decay<T>::type>(0)) == sizeof(char);
};
// Indicates whether the n-th type contains a tip static member
template<size_t n, typename... Ts>
struct nth_type_has_tip {
static const bool value = has_tip<typename std::tuple_element<n, std::tuple<Ts...>>::type>::value;
};
// Generic iteration
template<size_t index, size_t n>
struct TupleIter
{
template< typename... Ts >
typename std::enable_if< nth_type_has_tip<index, Ts...>::value , void >::type
static Iter(const std::tuple<Ts...>& tpl)
{
std::cout << "tip\n";
TupleIter<index + 1, n>::Iter(tpl );
}
template< typename... Ts >
typename std::enable_if< !nth_type_has_tip<index, Ts...>::value , void >::type
static Iter(const std::tuple<Ts...>& tpl) {
std::cout << "no tip\n";
TupleIter<index + 1, n>::Iter(tpl );
}
};
// Base class, we've reached the tuple end
template<size_t n>
struct TupleIter<n, n>
{
template<typename... Ts >
static void Iter( const std::tuple<Ts...>& tpl ) {
std::cout << "end\n";
}
};
// Helper function that forwards the first call to TupleIter<>::Iter
template<typename... Ts>
void iterate(const std::tuple<Ts...> &tup) {
TupleIter<0, sizeof...(Ts)>::Iter(tup);
}
int main() {
A a;
B b;
Z z;
std::tuple<const A&,const B&,const Z&> tup(a,b,z);
iterate(tup);
}
Here is another take on the question, very similar to mfontanini answer, but showcasing:
boost::fusion::for_each (instead of manually iterate over the tuple).
A variant for implementing has_type using an expression-based SFINAE approach, that I feel a little bit simpler to follow than the usual sizeof trick.
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/fusion/algorithm.hpp>
#include <iostream>
struct nat // not a type
{
private:
nat();
nat(const nat&);
nat& operator=(const nat&);
~nat();
};
template <typename T>
struct has_tip
{
static auto has_tip_imp(...) -> nat;
template <typename U>
static auto has_tip_imp(U&&) -> decltype(U::tip());
typedef decltype(has_tip_imp(std::declval<T>())) type;
static const bool value = !std::is_same<type, nat>::value;
};
struct CallTip
{
template<typename T>
typename std::enable_if<has_tip<T>::value>::type
operator()(T& t) const
{
std::cout << "tip\n";
T::tip();
}
template<typename T>
typename std::enable_if<!has_tip<T>::value>::type
operator()(T& t) const
{
std::cout << "no tip\n";
return;
}
};
struct A {
static void tip(){}
};
struct B {
static void tip(){}
};
struct Z {
};
int main()
{
A a;
B b;
Z z;
boost::tuple<const A&,const B&,const Z&> tpl(a, b, z);
boost::fusion::for_each(tpl, CallTip());
}
Note that if your compiler support variadic template you can use std::tuple instead of boost::tuple inside fusion::for_each by including #include<boost/fusion/adapted/std_tuple.hpp>
Edit :
As pointed by Xeo in the comment, it is possible to simplify a lot the expression-SFINAE approach by removing completely the trait has_tip and simply forward to a little call helper.
The final code is really neat and tight !
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/fusion/algorithm.hpp>
#include <iostream>
struct CallTip
{
template<typename T>
void operator()(const T& t) const
{
call(t);
}
template<class T>
static auto call(const T&) -> decltype(T::tip())
{
std::cout << "tip\n";
T::tip();
}
static void call(...)
{
std::cout << "no tip\n";
}
};
struct A {
static void tip(){}
};
struct B {
static void tip(){}
};
struct Z {
};
int main()
{
A a;
B b;
Z z;
boost::tuple<const A&,const B&,const Z&> tpl(a, b, z);
boost::fusion::for_each(tpl, CallTip());
}
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
Is it possible to create template to the initialization like:
template <typename C> typename C::value_type fooFunction(C& c) {...};
std::vector<string> vec_instance;
fooFunction(cont<0>(vec_instance));
fooFunction(cont<1>(vec_instance));
In general i'm interested is it possible to specify template using integer (ie. 0) instead of true type name.
And how to achieve above?
I'm not completely clear on what you're asking, but the following snippet works for me:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <typename C>
typename C::value_type fooFunction(const C & c) { return 0; };
/* note that fooFunction takes a ref-to-const, not a reference */
template<int N>
struct cont
{
public:
typedef int value_type;
cont(vector<string> vec) {};
};
int main()
{
std::vector<string> vec_instance;
fooFunction(cont<0>(vec_instance));
fooFunction(cont<1>(vec_instance));
}
Two changes worth noting:
An integer is not a type, so if cont is declared template <typename T>, what you have written will not work. template <int N> is the proper way to parameterize over an integral value, as templatetypedef mentioned.
I'm not sure how cont<> is defined, but from your usage it must be an object you are constructing as a temporary. You will have trouble passing this temporary as a reference into fooFunction. Note that my example above passes C as reference-to-const instead.
Yes, you can parameterize templates over non-type arguments like integers, pointers, and other templates. For example:
template <typename T, int N> struct Array {
T data[N];
/* ... other functions ... */
};
These templates work just like all the other templates you've seen, except that they're parameterized over integral values rather than types.
This link has some more info on the subject. "Modern C++ Design" and "C++ Templates: The Complete Guide" also have lots of info on how to do this.
Is this what you are after? Non-type template parameters:
template<int n> class Cont
{
public:
typedef int value_type;
};
template<>
class Cont<0>
{
public:
typedef double value_type;
value_type convert(const std::string& s) const
{
return atof(s.c_str());
}
};
template<>
class Cont<1>
{
public:
typedef long value_type;
value_type convert(const std::string& s) const
{
return atoi(s.c_str());
}
};
template <int n> typename Cont<n>::value_type fooFunction(const Cont<n>& cont, const std::string& s)
{
return cont.convert(s);
}
void test()
{
Cont<0> c0;
Cont<1> c1;
double d = fooFunction(c0,"1.0");
int i = fooFunction(c1, "-17");
}