Boost Fusion has been designed in such a way that most of the transformations are "lazy", in the sense that they all generate "views" but not actual (Fusion) containers (http://www.boost.org/doc/libs/1_58_0/libs/fusion/doc/html/fusion/algorithm.html). So for example to actually reverse a vector one needs to use the conversion function as_vector (http://www.boost.org/doc/libs/1_58_0/libs/fusion/doc/html/fusion/container/conversion/functions.html).
boost::fusion::vector<int, double, std::string> vec;
auto view_rev = boost::fusion::reverse(vec); // view object
auto vec_rev = boost::fusion::as_vector(view_rev);
Now, I want to do this with adapted std::tuple:
#include<boost/fusion/adapted/std_tuple.hpp>
...
std::tuple<int, double, std::string> tup;
auto view_rev = boost::fusion::reverse(tup);
auto tup_rev = boost::fusion::???(view_rev); // type should be of type std::tuple<std::string, double, int>
How do I convert the resulting view back to a tuple?
I expected this ??? function to be called as_std_tuple (in analogy to boost::fusion::as_vector, but it doesn't exists (yet?).
There a few solutions for reversing tuples, in this case I want just to use what is already in Boost Fusion.
I am not aware of any built-in method to convert a Boost Fusion Sequence into a std::tuple, but using the indices trick it can be implemented rather easily:
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
template<typename Sequence, std::size_t ...Is>
auto as_std_tuple_impl(const Sequence& s, indices<Is...>&&) -> decltype(std::tie(boost::fusion::at_c<Is>(s)...))
{
return std::tie(boost::fusion::at_c<Is>(s)...);
}
template <typename Sequence, typename Indices = build_indices<boost::fusion::result_of::size<Sequence>::value>>
auto as_std_tuple(const Sequence& s) -> decltype(as_std_tuple_impl(s, Indices()))
{
return as_std_tuple_impl(s, Indices());
}
Here is a full example that reverses an adapted std::tuple using boost::fusion::reverse and converts it back into a std::tuple and prints both tuples:
#include <tuple>
#include <utility>
#include<boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/algorithm/transformation/reverse.hpp>
#include <boost/fusion/include/reverse.hpp>
#include <boost/fusion/sequence/intrinsic/size.hpp>
#include <boost/fusion/include/size.hpp>
#include <iostream>
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
template<typename Sequence, std::size_t ...Is>
auto as_std_tuple_impl(const Sequence& s, indices<Is...>&&) -> decltype(std::tie(boost::fusion::at_c<Is>(s)...))
{
return std::tie(boost::fusion::at_c<Is>(s)...);
}
template <typename Sequence, typename Indices = build_indices<boost::fusion::result_of::size<Sequence>::value>>
auto as_std_tuple(const Sequence& s) -> decltype(as_std_tuple_impl(s, Indices()))
{
return as_std_tuple_impl(s, Indices());
}
template<class Tuple, std::size_t N>
struct TuplePrinter
{
static void print(const Tuple& t)
{
TuplePrinter<Tuple, N-1>::print(t);
std::cout << ", " << std::get<N-1>(t);
}
};
template<class Tuple>
struct TuplePrinter<Tuple, 1>
{
static void print(const Tuple& t)
{
std::cout << std::get<0>(t);
}
};
template<class... Args>
void print(const std::tuple<Args...>& t)
{
std::cout << "(";
TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
std::cout << ")\n";
}
int main()
{
std::tuple<int, double, std::string> tup(1,2.5,"hello");
auto view_rev = boost::fusion::reverse(tup);
auto reversed_tup = as_std_tuple(view_rev);
print(tup);
print(reversed_tup);
return 0;
}
output:
(1, 2.5, hello)
(hello, 2.5, 1)
Live example on ideone
Related
I've been trying to use Variadic Tuples on Generic Templates and Structs. The idea is to obtain the max and sum of a variadic tuple. So far I was able to obtain the required results but had to use Global static variables. I've posted the code below of the implementation.
#include <iomanip>
#include <complex>
#include <cmath>
#include <tuple>
#include <string>
#include <iostream>
using namespace std;
using namespace std::complex_literals;
template<typename T>
static T max;
template<typename T>
static T sum;
template<typename T>
static T average;
template <typename T, typename Tuple, std::size_t N>
struct Calculator
{
static void maximum(const Tuple& pack)
{
Calculator<T, Tuple, N - 1>::maximum(pack);
T packValue = get<N - 1>(pack);
if (packValue > max<T>)
{
//T max = get<0>(pack);
//Try the above instead of the below code to calculate max
max<T> = get<N - 1>(pack);
}
}
static void summation(const Tuple& pack)
{
Calculator<T, Tuple, N - 1>::summation(pack);
T packValue = get<N - 1>(pack);
sum<T> += packValue;
}
static void averager(const Tuple& pack)
{
average<T> = sum<T> / N;
}
};
template<typename T, typename Tuple>
struct Calculator<T, Tuple, 1>
{
static void maximum(const Tuple& pack)
{
//T max = get<0>(pack);
//Try the above instead of the below code to calculate max
max<T> = get<0>(pack);
}
static void summation(const Tuple& pack)
{
sum<T> = get<0>(pack);
}
};
int main()
{
tuple<double, double, double, double, double, double, double> t1 = make_tuple(16565.256, 45.539, 0.25, 1000.25, 1.25036, 35.66, 210.20);
Calculator<double, tuple<double, double, double, double, double, double, double>, 7>::maximum(t1);
cout << "Maximum is: " << max<double> << endl;
Calculator<double, tuple<double, double, double, double, double, double, double>, 7>::summation(t1);
cout << "Total Sum is: " << sum<double> << endl;
Calculator<double, tuple<double, double, double, double, double, double, double>, 7>::averager(t1);
cout << "Average is: " << average<double> << endl;
std::complex<int> c2(22, 3);
std::complex<int> ci(15, 41);
tuple<std::complex<int>, std::complex<int> > ct1 = make_tuple(ci, c2);
Calculator< std::complex<int>, tuple<std::complex<int>, std::complex<int> >, 2>::summation(ct1);
cout << "Summation of complex numbers is: " << sum<std::complex<int>> << endl;
}
My question, is it possible to implement a version that doesn't use Global static variables to hold the values of sum and max but an alternate implementation using Variadic Templates and Structs if possible?
You use variable template, so you have at least C++14. In C++14 you can use index_sequence as easy way to extract all tuple items and rewrite your recursion functions to be non-recursive:
template <typename T, typename Tuple, std::size_t N>
struct Calculator
{
template<size_t ... Indices>
static T maxHelper(const Tuple& pack, std::index_sequence<Indices...>) {
return std::max( {std::get<Indices>(pack)...} );
} // [1]
static T maximum(const Tuple& pack) {
return maxHelper(pack, std::make_index_sequence<N>{});
}
template<size_t ... Indices>
static T sumHelper(const Tuple& t, std::index_sequence<Indices...>) {
T arr[] = { std::get<Indices>(t)... }; // [2]
return std::accumulate(arr,arr+N,T{}); // [3]
}
static T summation(const Tuple& pack) {
return sumHelper(pack,std::make_index_sequence<N>{});
}
};
in [1] tuple's items are extracted into std::initializer_list, then std::max overload taking that list is called.
in [2] and [3] tuple's items are extracted into array, and summing is done by std::accumulate.
Demo
Since of C++17 use std::apply (and fold expression for summing):
template<class ... Args>
auto maxValue(std::tuple<Args...> t) {
return std::apply( [](auto&&... args) { return std::max({args...}); },t);
}
template<class ... Args>
auto sumValue(std::tuple<Args...> t) {
return std::apply( [](auto&&... args){ return (args + ...);} , t);
}
Demo
Consider the following code:
#include <iostream>
#include <utility>
#include <array>
#include <functional>
#include <ctime>
template <unsigned N> void foo() { std::cout << N << "(" << ") "; }
template<> void foo<2>() { std::cout << "TWO (" << ") "; }
struct Foo {
template <unsigned N> void operator()(std::integral_constant<unsigned,N>) { foo<N>(); }
};
template <std::size_t Offset, std::size_t... Idx, typename F>
void visit(F f, std::index_sequence<Idx...>, std::size_t n) {
static std::array<std::function<void()>, sizeof...(Idx)> funcs {{
[&f](){f(std::integral_constant<unsigned,Idx+Offset>{});}...
}};
funcs[n - Offset]();
};
template <std::size_t Start, std::size_t End, typename F>
void visit(F f, std::size_t n) {
visit<Start>(f, std::make_index_sequence<End-Start>{}, n);
};
int main() {
auto t = time(nullptr);
for(int i = 0; i < 10; i++) {
visit<1, 10>(Foo{}, (t+i) % 10);
}
}
This is valid C++14 (actually, also valid C++11 if you write your own std::index_sequence). But - it doesn't compile with g++ 6.x and 7.x; only g++ 8.x compiles it properly (see this happening on GodBolt).
For organizational reasons, I can require use g++ version up to 7.2. Is there a way I could alter the code while keeping the semantics, to make g++ 7.x compile it?
GCC's bug is that it can't pack expand lambdas. So don't pack expand lambdas.
template<class F, std::size_t Idx>
void caller(F& f) { f(std:::integral_constant<unsigned, Idx>()); }
template <std::size_t Offset, std::size_t... Idx, typename F>
void visit(F f, std::index_sequence<Idx...>, std::size_t n) {
using ptr_type = void (*)(F&);
static constexpr ptr_type funcs[] = {&caller<F, Idx+Offset>...};
funcs[n-Offset](f);
}
I hit two birds with one stone and expanded this visitation mechanism to functions taking an arbitrary number of arguments. And whaddaya know - that meant moving the lambda into a helper function to avoid different parameter packs interfering with each other. As #T.C. suggests - that's what GCC is having trouble doing, so the problem is avoided.
template <std::size_t N, typename F, typename... Ts>
std::function<void(Ts...)> make_visitor(F f) {
return
[&f](Ts... args) {
f(std::integral_constant<std::size_t,N>{}, std::forward<Ts>(args)...);
};
}
template <std::size_t Offset, std::size_t... Idx, typename F, typename... Ts>
void visit(F f, std::index_sequence<Idx...>, std::size_t n, Ts... args) {
static std::array<std::function<void(Ts...)>, sizeof...(Idx)> funcs {{
make_visitor<Idx+Offset, F, Ts...>(f)...
}};
funcs[n-Offset](std::forward<Ts>(args)...);
};
template <std::size_t Start, std::size_t End, typename F, typename... Ts>
void visit(F f, std::size_t n, Ts... args) {
visit<Start>(f, std::make_index_sequence<End-Start>{}, n, std::forward<Ts>(args)...);
};
I have a template class(CrMultiIndex) that receive as template parameter a definition of boost multi index(GlobalHash).
I need :
To add statistics to my template class according to Index used.
So i need a way to resize the vector(m_StatsByIndex) at init with the number of existing indices.
I still want the user to search according to tag and not index number.
So i need a way to convert from tag to index number so i can update statistics in vector according to index in vector.
I have template class
template <typename KeysType, typename MultiIndexType>
class CrMultiIndex
{
std::vector<SrStatisticsByIndex> m_StatsByIndex;
public:
MultiIndexType *m_pMultiIndex=NULL;
CrMultiIndex()
{
m_pMultiIndex = new MultiIndexType(typename
MultiIndexType::ctor_args_list());
}
Here is the definition of boost multi index container:
typedef boost::multi_index::multi_index_container<
CrUsersKeys,
UsersKey_hash_indices/*,
bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
> GlobalHash;
with a search function according to Tag
template <typename TagType,typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey & key)
{
return m_pMultiIndex->template get<TagType>().find(key) ;
}
Code is at http://coliru.stacked-crooked.com/a/d97195a6e4bb7ad4
You'd need to query the embedded index type lists:
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
namespace mpl = boost::mpl;
using tl = typename MultiIndexType::index_type_list;
using B = typename mpl::begin<tl>::type;
using helper = typename MultiIndexType::template index<Tag>;
static_assert(helper::index_found, "index not found");
auto N = mpl::distance<B, typename helper::iter>::value;
return N;
}
Or, using Boost Mpl all the way:
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
namespace mpl = boost::mpl;
using tl = typename MultiIndexType::index_type_list;
using B = typename mpl::begin<tl>::type;
using E = typename mpl::end<tl>::type;
using It = typename mpl::find_if<tl, bmi::detail::has_tag<Tag> >::type;
static_assert(not std::is_same<E, It>(), "index not found");
auto N = mpl::distance<B, It>::value;
return N;
}
You can use it like so:
template <typename TagType, typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey &key) {
auto& idx = m_pMultiIndex.template get<TagType>();
auto& stats = GetStats<TagType>();
auto it = idx.find(key);
++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);
return it;
}
DEMO
Note the code has been simplified:
Live On Coliru
#include <iostream>
#include <boost/multi_index/member.hpp> // for member
#include <boost/multi_index/hashed_index.hpp> // for hashed_unique
#include <boost/multi_index/ordered_index.hpp> // for ordered_non_unique
#include <boost/multi_index_container.hpp> // for multi_index_container
namespace bmi = boost::multi_index;
struct SrStatisticsByIndex {
int deleted;
int searchedSuccessfully;
int searchedNotFound;
};
template <typename MultiIndexType, typename ValueType = typename MultiIndexType::value_type>
class CrMultiIndex {
typedef typename MultiIndexType::index_type_list::size NumberOfIndexes;
template <typename Tag> constexpr static size_t IndexOfTag() {
using tl = typename MultiIndexType::index_type_list;
using B = typename boost::mpl::begin<tl>::type;
using helper = typename MultiIndexType::template index<Tag>;
static_assert(helper::index_found, "index not found");
return boost::mpl::distance<B, typename helper::iter>::value;
}
public:
MultiIndexType m_pMultiIndex;
template <typename Tag> SrStatisticsByIndex& GetStats()
{ return m_StatsByIndex.at(IndexOfTag<Tag>()); }
template <typename Tag> SrStatisticsByIndex const& GetStats() const
{ return m_StatsByIndex.at(IndexOfTag<Tag>()); }
// All the protected function are non locking function
template <typename TagType, typename SearchingKey>
typename MultiIndexType::template index<TagType>::type::iterator
GetIteratorBy(SearchingKey &key) {
auto& idx = m_pMultiIndex.template get<TagType>();
auto& stats = GetStats<TagType>();
auto it = idx.find(key);
++(it == idx.end()? stats.searchedNotFound : stats.searchedSuccessfully);
return it;
}
void Insert(ValueType const &key) {
std::cout << (m_pMultiIndex.insert(key).second? "success":"failed") << std::endl;
}
private:
std::vector<SrStatisticsByIndex> m_StatsByIndex { NumberOfIndexes() };
};
class CrUsersValue {
int val1;
int val2;
};
class CrUsersKeys {
public:
int IMSI;
int TIMESTAMP;
CrUsersValue val;
};
typedef boost::multi_index::multi_index_container<
CrUsersKeys,
bmi::indexed_by<
bmi::ordered_non_unique<bmi::tag<struct TIMESTAMP_tag>,
bmi::member<CrUsersKeys, int, &CrUsersKeys::TIMESTAMP> >,
bmi::hashed_unique<bmi::tag<struct IMSI_tag>,
bmi::member<CrUsersKeys, int, &CrUsersKeys::IMSI> /*, boost::hash<int>, std::equal_to<int>*/>
>
/*, bip::allocator<CrUsersKeys,bip::managed_shared_memory::segment_manager>*/
>
GlobalHash;
int main() {
CrMultiIndex<GlobalHash> multi;
CrUsersKeys key;
key.IMSI = 2;
multi.Insert(key);
int searchKey = 2;
auto it = multi.GetIteratorBy<IMSI_tag>(searchKey);
if (it != multi.m_pMultiIndex.get<IMSI_tag>().end())
std::cout << "found " << std::endl;
}
Prints
success
found
As a supplement to sehe's answer, this a rewrite of IndexOfTag that does not depend on undocumented Boost.MultiIndex features:
Live On Coliru
template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
using index_position<MultiIndexContainer,N+1>::case_of;
static constexpr std::size_t case_of(std::in_place_type_t<index_type>){return N;}
};
template<typename MultiIndexContainer>
struct index_position<
MultiIndexContainer,
boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
static constexpr void case_of(...){}
};
template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
return index_position<MultiIndexContainer>::case_of(std::in_place_type<index_type>);
}
Edit: In C++14:
Live On Coliru
template<typename MultiIndexContainer,std::size_t N=0>
struct index_position:index_position<MultiIndexContainer,N+1>
{
using index_type=typename boost::multi_index::nth_index<MultiIndexContainer,N>::type;
using index_position<MultiIndexContainer,N+1>::case_of;
static constexpr std::size_t case_of(index_type*){return N;}
};
template<typename MultiIndexContainer>
struct index_position<
MultiIndexContainer,
boost::mpl::size<typename MultiIndexContainer::index_type_list>::value
>
{
static constexpr void case_of(...){}
};
template <typename MultiIndexContainer,typename Tag>
constexpr std::size_t IndexOfTag()
{
using index_type=typename boost::multi_index::index<MultiIndexContainer,Tag>::type;
return index_position<MultiIndexContainer>::case_of((index_type*)(nullptr));
}
I need to convert a tuple to a byte array. This is the code I use to convert to byte array:
template< typename T > std::array< byte, sizeof(T) > get_bytes( const T& multiKeys )
{
std::array< byte, sizeof(T) > byteArr ;
const byte* start = reinterpret_cast< const byte* >(std::addressof(multiKeys) ) ;
const byte* end = start + sizeof(T);
std::copy(start, end, std::begin(byteArr));
return byteArr;
}
Here is how I call it:
void foo(T... keyTypes){
keys = std::tuple<T... >(keyTypes...);
const auto bytes = get_bytes(keys);
}
I need to augment this code such that when a pointer is a part of the tuple, I dereference it to it's value and then pass the new tuple, without any pointers, to the get_bytes() function. How do I detect the presence of a pointer in the tuple? I can then iterate through the tuple and dereference it with:
std::cout << *std::get<2>(keys) << std::endl;
Add a trivial overload: T get_bytes(T const* t) { return getBytes(*t); }.
That would be easy with C++14 :
#include <iostream>
#include <tuple>
#include <utility>
template <class T> decltype(auto) get_dereferenced_value(T &&value) {
return std::forward<T>(value);
}
template <class T> decltype(auto) get_dereferenced_value(T *value) {
return *value;
}
template <class Tuple, class Indexes> struct get_dereferenced_tuple_impl;
template <class... Args, size_t... Index>
struct get_dereferenced_tuple_impl<std::tuple<Args...>,
std::integer_sequence<size_t, Index...>> {
decltype(auto) operator()(std::tuple<Args...> const &originalTuple) {
return std::make_tuple(
get_dereferenced_value(std::get<Index>(originalTuple))...);
}
};
template <class Tuple>
decltype(auto) get_dereferenced_tuple(Tuple const &tupleValue) {
return get_dereferenced_tuple_impl<
Tuple,
std::make_integer_sequence<size_t, std::tuple_size<Tuple>::value>>{}(
tupleValue);
}
int main() {
char c = 'i';
std::tuple<char, char *> t{'h', &c};
auto t2 = get_dereferenced_tuple(t);
std::cout << std::get<0>(t2) << std::get<1>(t2) << "\n";
return 0;
}
If you cannot use C++14, then you would have to write more verbose decltype expressions, as well as include an implementation of std::(make_)integer_sequence.
This has a drawback though : copies will be made before copying the bytes. A tuple of references is not a good idea. The most performant version would be a get_bytes able to serialize the entire mixed tuple directly.
I would try to make my point clear with an example:
We have
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
But I'm thinking if it is ok to make it more convenient:
template <typename T> void sort(std::vector<T>& container) {
std::sort( container.begin(), container.end() );
}
template <typename T> void sort(std::list<T>& container);
template <typename T> void sort(std::array<T>& container);
//e.t.c
You know there are many container types, it is possible to code once for all the container types?
void sort(ContainerType<ElementType> &container);
//and container should have begin() and end() methods,
//otherwise the compiler would warn me.
You are talking about concepts in C++. The idea is discussed for a long time for now, but they are still not in the standard. See here:
template<Sortable Cont>
void sort(Cont& container);
The work is close to the end for now, several experimental implementations are already available, and we expect them to hit C++17, hopefully. The nicest thing about concepts is their straightforward error messages:
list<int> lst = ...; // oops, bidirectional iterators
sort(lst); // error: 'T' is not a/an 'Sortable' type
In modern compilers, errors related to templatized code are very confusing. Compare with this example, compiled with Visual Studio 2013:
std::list<int> l;
std::sort(l.begin(), l.end());
// error C2784: 'unknown-type std::operator -(std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)' : could not deduce template argument for 'std::move_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::_Revranit<_RanIt,_Base> &,const std::_Revranit<_RanIt2,_Base2> &)' : could not deduce template argument for 'const std::_Revranit<_RanIt,_Base> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
There is even a tag on SO: c++-concepts.
Its easy to write a sort function that works for any container. Just write:
template<class C>
void sort(C& container) {
std::sort( container.begin(), container.end() );
}
However, if you want your sort function to be picked ONLY for containers, it becomes a little bit more difficult: As concepts are not yet available, you have to write your own type trait for all containers and use SFINAE.
Edit: Come to think of it, as any class with a random access iterator is probably a container anyway, it should be enough to write the following (without the need for a container trait):
#include <type_traits>
template<class C>
typename std::enable_if<std::is_same<typename std::iterator_traits<typename C::iterator>::iterator_category, std::random_access_iterator_tag>{}>::type
sort(C& container) {
std::sort( container.begin(), container.end() );
}
#include <iterator>
#include <utility>
#include <type_traits>
namespace detail
{
using std::begin;
template <typename T, typename = void>
struct has_begin : std::false_type {};
template <typename T>
struct has_begin<T, decltype(void(begin(std::declval<T&>())))> : std::true_type {};
using std::end;
template <typename T, typename = void>
struct has_end : std::false_type {};
template <typename T>
struct has_end<T, decltype(void(end(std::declval<T&>())))> : std::true_type {};
}
template <typename T> using has_begin = detail::has_begin<T>;
template <typename T> using has_end = detail::has_end<T>;
Usage:
template <typename ContainerType>
void sort(ContainerType& container)
{
static_assert(has_begin<ContainerType>{} && has_end<ContainerType>{},
"Invalid container type");
}
Tests:
#include <vector>
#include <list>
namespace X
{
struct A {};
A* begin(A&) { return {}; }
A* end(A&) { return {}; }
}
struct B {};
int main()
{
std::vector<int> v; sort(v); // OK
std::list<int> l; sort(l); // OK
X::A a; sort(a); // OK
int arr[3]{}; sort(arr); // OK
B b; sort(b); // error: Invalid container type
}
DEMO
At the time of answering this question, MikeMB's answer gives the approach but doesn't compile. But here is my attempt. A much lesser complicated approach. You will have to overload SortHelper to accept comparator as well.
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
template<typename C>
void SortHelper(C& container, std::random_access_iterator_tag)
{
std::sort(std::begin(container), std::end(container));
}
template<typename C>
void SortHelper(C& container, std::bidirectional_iterator_tag)
{
container.sort();
}
template<class C>
void sort(C& container)
{
SortHelper(container, typename std::iterator_traits<typename C::iterator>::iterator_category());
}
int main()
{
std::vector<int> ints1 { 3, 2, 1 };
std::list<int> ints2 { 3, 2, 1 };
sort(ints1);
sort(ints2);
std::cout << "printing ints1\n";
for (auto e : ints1 ) { std::cout << e << "\n" ; }
std::cout << "printing ints2\n";
for (auto e : ints2 ) { std::cout << e << "\n" ; }
}
Output
printing ints1
1
2
3
printing ints2
1
2
3