Determine If a Type Is an Stl Container At Compile Time

Determine if a type is an STL container at compile time

First, you define your primary template, which will have a member which is false in the default case:

template <typename T>
struct is_cont {
static const bool value = false;
};

Then you will define partial specializations for your container types which have a value of true instead:

template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};

Then for a type X that you want to check, use it like

if (is_cont<X>::value) { ... } 

Determine if a type is a container at compile time in c++98

I found this - cxx-prettyprint and will adapt it to my needs.

How can I know the C++ template is a container or a type?

You have several options at your disposal.

  • If you want a default behaviour, and change it only for one type (or a few), use template specialization for your function.

For example :

template<typename T>
void myfunc() { /*default*/ }


template<>
void myfunc<int>() { /*specialized version for int */}
  • If you want to change the behaviour of your functions for generic groups of types, you can use Type Traits (something like std::is_fundamental ). You might have to implement your own type traits in thiscase.

Selecting container type at compile time

The simplest way seems to be

using Container = std::conditional_t< std::is_same_v<T, int>, 
std::vector<T>, // T is int
std::set<T>>;
Container bar;

std::conditional_t allows you to select a type. There is no standard conditional template that would allow to select a template. You can write one if you want but this won't be convenient to use.

template <bool t, template <typename...> typename X, template <typename...> typename Y>
struct conditional_template;

template <template <typename...> typename X, template <typename...> typename Y>
struct conditional_template<true, X, Y>
{
template <typename... ts> using type = X<ts...>;
};

template <template <typename...> typename X, template <typename...> typename Y>
struct conditional_template<false, X, Y>
{
template <typename... ts> using type = Y<ts...>;
};

template <typename... ts>
using Container = conditional_template<true, std::list, std::vector>::type<ts...>;

Container<int> x;

It doesn't seem possible to define an analogue of std::conditional_t convenience alias.

Determine data type of items in an STL container in a template method

You could use template template parameters:

template <template <class, class...> class CONTAINER, class ITEM, class... REST>
std::string doStuff(const CONTAINER<ITEM, REST...>& container) {
using CONTAINER_TYPE = ITEM; // or `typename CONTAINER<ITEM, REST...>::value_type`
// This requires SFINAE to not match `ITEM`s without `value_type`:
using ITEM_TYPE = typename ITEM::value_type;

//...
}

Check if function return type is the same as an STL container type value

Not sure to understand completely but... if the "generic funcion" isn't a generic-lambda or a template operator() in a class/struct... you tagged C++17 so you can use deduction guides so you can deduce the type returned from the function using std::function's deduction guides.

Something as

decltype(std::function{std::declval<Function>()})::result_type

For the value type of the container is usually available the value_type type.

So, defining a couple of using types inside the body of the struct, you can write

template <typename F, typename C>
struct task
{
using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
using vtype = typename C::value_type;

// ...

task (F func, C & cont) : f{func}, c{cont}
{ static_assert( std::is_same<rtype, vtype>{} );}
};

But observe that the static_assert() inside the constructor use only elements that aren't specific of the constructor.

This way, if you have to develop (by example) ten constructors, you have to write ten times the same static_assert() inside the ten constructors bodies.

I suggest to place the static_assert() inside the body of the struct so you have to write it only one time.

I mean

template <typename F, typename C>
struct task
{
using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
using vtype = typename C::value_type;

static_assert( std::is_same<rtype, vtype>{} );

// ...
};

The following is a full compiling example

#include <vector>
#include <functional>

template <typename F, typename C>
struct task
{
using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
using vtype = typename C::value_type;

static_assert( std::is_same<rtype, vtype>{} );

F f;
C & c;

task (F func, C & cont) : f{func}, c{cont}
{ }
};

int multiply (int x)
{ return x*10; }

int main ()
{
std::vector<int> v;

int c=10;

auto stateless = [](float x){ return x*10;};
auto stateful = [&c](int x){ return x*c;};

task t1(multiply, v); // compile
task t2(stateful, v); // compile
task t3(stateless, v); // compilation error
}

But remember: this function doen't works with generic-lambdas.

In that case I don't know how to solve the problem and I suppose isn't solvable at all without knowing the type of the input parameters.

check type of element in stl container - c++

For containers in general it will be X::value_type. For associative containers it will be X::mapped_type (X::value_type corresponds to pair<const Key,T>). It is according to Chapter 23 of C++ Standard.

To check that types are equal you could use boost::is_same. And since C++11 — std::is_same.

How to check whether a type is std::vector::iterator at compile time?

Well, in the simplest case scenario it could look something like this:

#include <type_traits>
#include <vector>
#include <list>
#include <cstdio>

template <typename T>
typename std::enable_if<
std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value
, void>::type
do_something (T begin, T end)
{
std::printf ("Got vector iterators!\n");
}

template <typename T>
typename std::enable_if<
!std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value
, void>::type
do_something (T begin, T end)
{
std::printf ("Got something other than vector iterators!\n");
}

template <typename T>
typename std::enable_if<std::is_pod<T>::value, void>::type
do_something (T begin, T end)
{
std::printf ("Got some POD iterators!\n");
}

int main()
{
std::vector<int> ivec;
std::list<int> ilist;
char cdata[64];

do_something (ivec.begin (), ivec.end ());
do_something (ilist.begin (), ilist.end ());
do_something (&cdata[0], cdata + 32);

return 0;
}

But the real problem comes when someone decides to use allocator different from default one. Since you want to check iterator against some well known type, not a well known template, then you can basically use this and possibly extend it with some allocators that you are aware of. Otherwise, a template instantiated with different types is a different type, and I am not sure if there is a way to test if a type is an instance of template specialized with some arbitrary parameter, there is probably no such way.

On the other hand, you may solve this problem differently. For example, what difference it makes whether this is std::vector<...> iterator or not? It might make sense to check whether it is random access or not, etc.

UPDATE:

For continuously laid out memory, I'd say the best bet is to use iterator traits and check for random access tag. For example:

#include <type_traits>
#include <functional>
#include <vector>
#include <list>
#include <cstdio>

template <typename T>
struct is_random_access_iterator : std::is_same<
typename std::iterator_traits<T>::iterator_category
, std::random_access_iterator_tag>
{};

template <typename T>
typename std::enable_if<is_random_access_iterator<T>::value>::type
do_something (T begin, T end)
{
std::printf ("Random access granted!\n");
}

template <typename T>
typename std::enable_if<!is_random_access_iterator<T>::value>::type
do_something (T begin, T end)
{
std::printf ("No random access for us today!\n");
}

int main()
{
std::vector<int> ivec;
std::list<int> ilist;
char cdata[32];

do_something (ivec.begin (), ivec.end ());
do_something (ilist.begin (), ilist.end ());
do_something (&cdata[0], cdata + sizeof (cdata) / sizeof (cdata[0]));

return 0;
}

This one will be definitely simpler and even more solid than checking against std::vector with allocators. However, even in this case someone can fool you if they really want, buy providing you random access iterator that provides seamless access to different chunks of memory, and you will have big problems once you convert that into a pointer use pointer arithmetics rather than iterator's overloaded operators. You can protect yourself against that only by comparing memory addresses while changing both raw pointer and iterator, but there is no juice.

Hope it helps.

Check for container type

You can use function overloading to achieve that:

template<typename T>
void func(std::vector<T>& vec)
{
//Do something with vector
}

template<typename T>
void func(std::list<T>& list)
{
//Do something with list
}

Or using typeid, which is probably less than ideal, because the code in either case would have to be compilable for both std::vector and std::list, as templates are known at compile time, and even though the branch might not execute on a std::list, the compiler doesn't know this at that point, and so it will fail to compile, trying to apply a std::vector operation on a std::list.

template<template<typename, typename> class C, typename T, typename Alloc>
void func(C<T, Alloc>& container)
{
if (typeid(container).hash_code() == typeid(std::vector<T, Alloc>&).hash_code())
; //Do something with vector
else if (typeid(container).hash_code() == typeid(std::list<T, Alloc>&).hash_code())
; //Do something with list
}

Check at compile-time is a template type a vector

It is named tag dispatching :

#include <vector>
#include <set>
#include <type_traits>

template<typename T> struct is_vector : public std::false_type {};

template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};

template <typename T>
class X {
T container;

void foo( std::true_type ) {
container.push_back(0);
}
void foo( std::false_type ) {
container.insert(0);
}
public:
void foo() {
foo( is_vector<T>{} );
}
};

// somewhere else...
int main() {
X<std::vector<int>> abc;
abc.foo();

X<std::set<int>> def;
def.foo();
}


Related Topics



Leave a reply



Submit