I have a function that currently accepts 2 vectors that can contain any plain old data ...
template <class T>
void addData(const vector<T>& yData, vector<T> xData)
{ .. }
Question:
Would it be possible to modify it to take two std::array or two std::vector, or even a combination thereof, given that these containers take a different number of template arguments?
Sure, it's just a matter of creating a suitable type trait. The example just uses a function f() with one argument but it is trivial to extend to take any number of arguments.
#include <array>
#include <vector>
#include <deque>
#include <utility>
#include <cstddef>
template <typename T>
struct is_array_or_vector {
enum { value = false };
};
template <typename T, typename A>
struct is_array_or_vector<std::vector<T, A>> {
enum { value = true };
};
template <typename T, std::size_t N>
struct is_array_or_vector<std::array<T, N>> {
enum { value = true };
};
template <typename T>
typename std::enable_if<is_array_or_vector<T>::value>::type
f(T const&)
{
}
int main()
{
f(std::vector<int>()); // OK
f(std::array<int, 17>()); // OK
f(std::deque<int>()); // ERROR
}
Why not just use this, which works with any container using random-access iterators, including plain old arrays. If you can use iteration instead of indexing, you can do away with the random-access requirement as well.
template <typename Cnt1, typename Cnt2>
void addData(const Cnt1& yData, Cnt2 xData) // is pass-by-value intended?
{
using std::begin;
using std::end;
typedef decltype(*begin(yData)) T;
const auto sizeY = end(yData) - begin(yData);
const auto sizeX = end(xData) - begin(xData);
// ...
}
C++03 version (doesn't support plain old arrays):
template <typename Cnt1, typename Cnt2>
void addData(const Cnt1& yData, Cnt2 xData) // is pass-by-value intended?
{
typedef Cnt1::value_type T;
const size_t sizeY = yData.end() - yData.begin();
const size_t sizeX = xData.end() - xData.begin();
// ...
}
An alternative solution:
#include <iostream>
#include <vector>
#include <array>
using std::vector;
using std::array;
template <typename Container>
struct container_helper; // undefined
template <typename T>
struct container_helper<vector<T>>
{
explicit container_helper(vector<T>& data)
: _data(data)
{}
T* get_data()
{ return &_data[0]; }
size_t get_size()
{ return _data.size(); }
private:
vector<T>& _data;
};
template <typename T, size_t N>
struct container_helper<array<T,N>>
{
explicit container_helper(array<T,N>& data)
: _data(data)
{}
T* get_data()
{ return &_data[0]; }
size_t get_size()
{ return N; }
private:
array<T,N>& _data;
};
template <typename Container1, typename Container2>
void add_data(Container1& c1, Container2& c2)
{
container_helper<Container1> c1_helper(c1);
container_helper<Container2> c2_helper(c2);
/* do whatever you want with the containers */
std::cout << "c1 size " << c1_helper.get_size() << std::endl;
std::cout << "c2 size " << c2_helper.get_size() << std::endl;
}
int main()
{
vector<int > v_ints(3);
array<int, 2> a_ints;
add_data(v_ints, a_ints);
}
Related
I have a template that looks like this
template <typename T> class Foo
{
public:
Foo(const T& t) : _t(t) {}
private:
const T _t;
};
Is there a savvy template metaprogramming way to avoid using a const reference in cases where the argument type is trivial like a bool or char? like:
Foo(stl::smarter_argument<T>::type t) : _t(t) {}
I think the right type trait is is_scalar. This would work as follows:
template<class T, class = void>
struct smarter_argument{
using type = const T&;
};
template<class T>
struct smarter_argument<T, std::enable_if_t<std::is_scalar_v<T>>> {
using type = T;
};
Edit:
The above is still a bit old-school, thanks #HolyBlackCat for reminding me of this more terse version:
template<class T>
using smarter_argument_t = std::conditional_t<std::is_scalar_v<T>, T, const T&>;
I would suggest to use sizeof(size_t) (or sizeof(ptrdiff_t)) which returns a "typical" size related to your machine with the hope that any variable of this size fits into a register. In that case you can safely pass it by value. Moreover, as suggested by #n314159 (see comments at the end of this post) it is useful to ensure that the variable is also trivialy_copyable.
Here is a C++17 demo:
#include <array>
#include <ccomplex>
#include <iostream>
#include <type_traits>
template <typename T>
struct maybe_ref
{
using type = std::conditional_t<sizeof(T) <= sizeof(size_t) and
std::is_trivially_copyable_v<T>, T, const T&>;
};
template <typename T>
using maybe_ref_t = typename maybe_ref<T>::type;
template <typename T>
class Foo
{
public:
Foo(maybe_ref_t<T> t) : _t(t)
{
std::cout << "is reference ? " << std::boolalpha
<< std::is_reference_v<decltype(t)> << std::endl;
}
private:
const T _t;
};
int main()
{
// with my machine
Foo<std::array<double, 1>> a{std::array<double, 1>{}}; // <- by value
Foo<std::array<double, 2>> b{std::array<double, 2>{}}; // <- by ref
Foo<double> c{double{}}; // <- by value
Foo<std::complex<double>> d{std::complex<double>{}}; // <- by ref
}
I would make use of the C++20 keyword requires. Just like that:
#include <iostream>
template<typename T>
class Foo
{
public:
Foo(T t) requires std::is_scalar_v<T>: _t{t} { std::cout << "is scalar" <<std::endl; }
Foo(const T& t) requires (not std::is_scalar_v<T>): _t{t} { std::cout << "is not scalar" <<std::endl;}
private:
const T _t;
};
class cls {};
int main()
{
Foo{true};
Foo{'d'};
Foo{3.14159};
cls c;
Foo{c};
return 0;
}
You can run the code online to see the following output:
is scalar
is scalar
is scalar
is not scalar
I want to find all objects of given types and add them to vector.
For now I have code:
template<class T>
void fill1(std::vector<Character*> &vec2)
{
for (int i = 0; i < GameObject::allObjects.size(); i++)
{
if (dynamic_cast<T>(GameObject::allObjects[i]))
{
vec2.push_back(dynamic_cast<Character*>(GameObject::allObjects[i]));
}
}
}
template<class First, class ...T>
void fill2(std::vector<Character*> &vec2)
{
fill1<First>(vec2);
fill2<T...>(vec2);
}
template<class ... T>
std::vector<Character*> SpecialList<T...>::get()
{
std::vector<Character*> vec2;
fill2<T...>(vec2);
return vec2;
}
The code doesn't compile at all.
The error we are getting is:
could not deduce template argument for 'First'
I know that all the given types are inherited from class Character and I have a vector of all my objects (GameObject::allObjects).
Instead of recursion use parameter pack expansion inside list initialization of a dummy array e.g.:
#include <vector>
#include <memory>
#include <tuple>
#include <iostream>
#include <algorithm>
template <class... Ts>
struct tag { };
template <class T>
struct Predicate {
template <class U>
bool operator()(std::shared_ptr<U> sp) const {
return std::dynamic_pointer_cast<T>(sp) != nullptr;
}
};
template <class... Ts, class T>
std::vector<std::shared_ptr<T>> all(std::vector<std::shared_ptr<T>> &v, tag<Ts...>) {
std::vector<std::shared_ptr<T>> result;
int dummy[] {(std::copy_if(v.begin(), v.end(), std::back_inserter(result), Predicate<Ts>{}),0)...};
static_cast<void>(dummy);
return result;
}
struct A {
virtual ~A() = default;
};
struct B: A { };
struct C: A { };
int main() {
std::vector<std::shared_ptr<A>> v { std::make_shared<A>(),
std::make_shared<A>(),
std::make_shared<B>(),
std::make_shared<B>(),
std::make_shared<C>(),
std::make_shared<C>() };
std::cout << all(v, tag<B, C>{}).size() << std::endl;
}
[live demo]
My class:
template < typename T >
Array<T>{};
(Source data is stored in vector)
I have an object:
Array< string > a;
a.add("test");
And I have an object:
Array< Array< string > > b;
b.add(a);
How can I check:
Is b[0] an instance of Array (regardless of template type)?
Is a[0] an instance of any type except Array?
If you can use C++11, creating your type traits; by example
#include <string>
#include <vector>
#include <iostream>
#include <type_traits>
template <typename T>
struct Array
{
std::vector<T> v;
void add (T const t)
{ v.push_back(t); }
};
template <typename>
struct isArray : public std::false_type
{ };
template <typename T>
struct isArray<Array<T>> : public std::true_type
{ };
template <typename T>
constexpr bool isArrayFunc (T const &)
{ return isArray<T>::value; }
int main()
{
Array<std::string> a;
Array<Array<std::string>> b;
a.add("test");
b.add(a);
std::cout << isArrayFunc(a.v[0]) << std::endl; // print 0
std::cout << isArrayFunc(b.v[0]) << std::endl; // print 1
}
If you can't use C++11 or newer but only C++98, you can simply write isArray as follows
template <typename>
struct isArray
{ static const bool value = false; };
template <typename T>
struct isArray< Array<T> >
{ static const bool value = true; };
and avoid the inclusion of type_traits
--- EDIT ---
Modified (transformed in constexpr) isArrayFunc(), as suggested by Kerrek SB (thanks!).
Below is a shorter version of the solution proposed by max66 that no longer uses struct isArray.
It works in C++98 and later revisions.
#include <string>
#include <vector>
#include <iostream>
template <typename T>
struct Array
{
std::vector<T> v;
void add (T const t)
{ v.push_back(t); }
};
template <typename T>
constexpr bool isArrayFunc (T const &)
{ return false; }
template <typename T>
constexpr bool isArrayFunc (Array<T> const &)
{ return true; }
int main()
{
Array<std::string> a;
Array<Array<std::string>> b;
a.add("test");
b.add(a);
std::cout << isArrayFunc(a.v[0]) << std::endl; // print 0
std::cout << isArrayFunc(b.v[0]) << std::endl; // print 1
}
in c++ you can use
if(typeid(obj1)==typeid(ob2))//or typeid(obj1)==classname
cout <<"obj1 is instance of yourclassname"
in your case you can check that with typeid(obj1)==std::array
I am new to SFINAE. I have a template that I would like to be able to accept classes that the size could be determined simply calling sizeof(x) or in case the value is dynamic it will require x.size().
I am trying to wrap my head around how as smooth as possible this could looks like and I think interface: size_t size(const Item& item) seems to be good enough.
The following is an example that works:
#include <iostream>
#include <cstdio>
#include <type_traits>
template <typename T>
class Fixed {
public:
typedef T Item;
static const bool kFixedSize = true;
static size_t size() {
return sizeof(T);
}
};
template <typename T>
class Dynamic {
public:
typedef T Item;
static const bool kFixedSize = false;
static size_t size(const T& item) {
return item.size();
}
};
template <typename T>
class Serialize {
public:
template <typename = typename std::enable_if<T::kFixedSize> >
size_t size(typename T::Item&) {
return T::size();
}
template <typename = typename std::enable_if<!T::kFixedSize> >
size_t size(const typename T::Item& item) {
return T::size(item);
}
};
int main() {
Serialize< Fixed<int> > fixed;
int a = 0;
std::cout << fixed.size(a) << std::endl;
Serialize< Dynamic<std::string> > dynamic;
std::cout << dynamic.size("string") << std::endl;
return 0;
}
It has an issues though one is: size_t size(typename T::Item&) and the other is size_t size(const typename T::Item& item) else the compiler compliance that I am overloading the template. The second is it seems like too match very tricky code to achieve the goal - is there better ways to do this?
I believe you want something like this
//class hierarchy to set the priority for type matching
struct second_priority
{
};
struct first_priority : public second_priority
{};
template<typename T>
auto size_impl(T const & data, second_priority t) -> int
{
return sizeof(data);
}
template<typename T>
auto size_impl(T const & data , first_priority t) -> decltype(data.size(),int())
{
return data.size();
}
template<typename T>
int size(T const & data )
{
return size_impl(data,first_priority{});
}
I think #Gautam Jha presented a nice solution using SFINAE. You can shorten it a bit by using ellipsis for the 'else' case, so you don't need to use this auxiliary class and it's inheritance:
template<typename T>
auto size_impl(T const & item, int) -> decltype(item.size())
{
return item.size();
}
template<typename T>
auto size_impl(T const & item, ...) -> size_t
{
return sizeof(T);
}
template<typename T>
auto size(T const & item) -> size_t
{
return size_impl(item, 0);
}
It's cool that you're playing around with SFINAE, but usually there are simpler (i.e. to read and to understand) ways to achieve the same, see the solution of POW (which has unfortunately been deleted).
Since all you want to do is call different functions to get the size in Dynamic or Fixed, you can just implement these classes differently and use them in Serialize:
#include <iostream>
#include <cstdio>
#include <type_traits>
template <typename T>
class Fixed {
public:
typedef T Item;
static size_t size(const T&) {
return sizeof(T);
}
};
template <typename T>
class Dynamic {
public:
typedef T Item;
static size_t size(const T& item) {
return item.size();
}
};
template <typename T>
class Serialize {
public:
size_t size(typename T::Item const& x) {
return T::size(x);
}
};
int main() {
Serialize< Fixed<int> > fixed;
int a = 0;
std::cout << fixed.size(a) << std::endl;
Serialize< Dynamic<std::string> > dynamic;
std::cout << dynamic.size( std::string{"string"} ) << std::endl;
return 0;
}
However, I would consider using a type-trait or a free function to do the same. This would be more extensible, because you have to just provide a new trait or an overload for new types, e.g. some container which has only a length method.
#include <iostream>
#include <cstdio>
#include <type_traits>
size_t size(int) {return sizeof(int);}
size_t size(std::string const& s) {return s.size();}
template<typename T>
struct size_trait
{
};
template<>
struct size_trait<int>
{
static size_t size(int) {return sizeof(int);}
};
template<>
struct size_trait<std::string>
{
static size_t size(std::string const& x) {return x.size();}
};
template <typename T>
class Serialize {
public:
size_t size(T const& x) {
return ::size(x);
}
size_t size_(T const& x) {
return size_trait<T>::size(x);
}
};
int main() {
Serialize< int > fixed;
int a = 0;
std::cout << fixed.size(a) << std::endl;
std::cout << fixed.size_(a) << std::endl;
Serialize< std::string > dynamic;
std::cout << dynamic.size( std::string{"string"} ) << std::endl;
std::cout << dynamic.size_( std::string{"string"} ) << std::endl;
return 0;
}
I found the following code which I am using as a basis. It describes how to populate a boost::fusion::vector with the values 6:
#include <boost/fusion/algorithm.hpp>
#include <boost/fusion/container.hpp>
struct F {
F(int blah): blah(blah){}
template <typename T>
void operator()(T& t) const {
t = blah;
}
int blah;
};
template <typename T>
void apply_for_each_to_assign(T &t)
{
boost::fusion::for_each(t, F(6));
}
int main() {
boost::fusion::vector<int, double, int> idi;
apply_for_each_to_assign(idi);
}
My question is this, instead of populating every element with the value 6- what would be the neatest way to have every element incrementing by one? So
v[0] = 1
v[1] = 2
etc? I presume I would need to write an increment function but I am not sure how I need to incorporate it in to the above code?
You can use fold.
fold takes a function A f(A, B) where B is your element type, and a intial A value and call it on all the elements. It is rougly equivalent to f(f(f(initial, first_elem), second_elem), third_elem).
You should declare the functor like this:
struct accumulator
{
typedef int result_type;
template<typename T>
int operator()(int value, T& t) const
{
t += value;
return value + 1;
}
};
And here's the usage:
template <typename T>
void apply_for_each_to_assign(T &t)
{
boost::fusion::fold(t, 1, accumulator());
}
Here a whole example with printing:
#include <boost/fusion/algorithm/iteration/fold.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/include/fold.hpp>
#include <boost/fusion/container.hpp>
#include <iostream>
struct accumulator
{
typedef int result_type;
template<typename T>
int operator()(int value, T& t) const
{
t += value;
return value + 1;
}
};
struct Print {
template <typename T>
void operator()(T& t) const {
std::cout << t << std::endl;
}
};
template <typename T>
void apply_for_each_to_assign(T &t)
{
boost::fusion::fold(t, 1, accumulator());
boost::fusion::for_each(t, Print());
}
int main()
{
boost::fusion::vector<int, double, int> idi;
apply_for_each_to_assign(idi);
}