Need help linking code using Boost rtree for storing user-defined object in tree where object contains the indexable -a boost geometry box - boost

I have a hard time following the C++ template and stl code of the open source boost library and hope someone can help with my code sample that tries to store a user defined object (Node) in a boost::geometry::index::rtree. The Node object contains the indexable and I am trying to define an indexable object that translates the Node to a box (retrieves the box member of Node).
I am using Visual Studio Express 2022 and Boost 1.79.0 under Windows 10.
This is the link error:
Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.
Automatic MOC and UIC for target BoostNodeTree
BoostNodeTree.cpp
E:\boost_1_79_0\boost/geometry/index/detail/translator.hpp(62,1): error C2662: 'const boost::geometry::index::node_indexable::result_type &boost::geometry::index::node_indexable::operator ()(const Node &)': cannot convert 'this' pointer from 'const boost::geometry::index::detail::translator<IndexableGetter,EqualTo>' to 'boost::geometry::index::node_indexable &' [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
with
[
IndexableGetter=NodeTree::indexable_getter,
EqualTo=NodeTree::equal_to_getter
]
E:\boost_1_79_0\boost/geometry/index/detail/translator.hpp(62,33): message : Conversion loses qualifiers [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
C:\Users\Lawrence Travis\source\repos\BoostNodeTree\NodeTree.h(22,36): message : see declaration of 'boost::geometry::index::node_indexable::operator ()' [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
E:\boost_1_79_0\boost/geometry/index/rtree.hpp(1655): message : see reference to function template instantiation 'boost::geometry::model::box<point_t> boost::geometry::index::detail::translator<IndexableGetter,EqualTo>::operator ()<Node>(const Value &) const' being compiled [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
with
[
IndexableGetter=NodeTree::indexable_getter,
EqualTo=NodeTree::equal_to_getter,
Value=Node
]
E:\boost_1_79_0\boost/geometry/index/rtree.hpp(1655): message : see reference to function template instantiation 'boost::geometry::model::box<point_t> boost::geometry::index::detail::translator<IndexableGetter,EqualTo>::operator ()<Node>(const Value &) const' being compiled [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
with
[
IndexableGetter=NodeTree::indexable_getter,
EqualTo=NodeTree::equal_to_getter,
Value=Node
]
E:\boost_1_79_0\boost/geometry/index/rtree.hpp(1652): message : while compiling class template member function 'void boost::geometry::index::rtree<NodeTree::value,NodeTree::parameters,NodeTree::indexable_getter,NodeTree::equal_to_getter,boost::container::new_allocator<Value>>::raw_insert(const Node &)' [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
with
[
Value=NodeTree::value
]
E:\boost_1_79_0\boost/geometry/index/rtree.hpp(832): message : see reference to function template instantiation 'void boost::geometry::index::rtree<NodeTree::value,NodeTree::parameters,NodeTree::indexable_getter,NodeTree::equal_to_getter,boost::container::new_allocator<Value>>::raw_insert(const Node &)' being compiled [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
with
[
Value=NodeTree::value
]
C:\Users\Lawrence Travis\source\repos\BoostNodeTree\NodeTree.h(56): message : see reference to class template instantiation 'boost::geometry::index::rtree<NodeTree::value,NodeTree::parameters,NodeTree::indexable_getter,NodeTree::equal_to_getter,boost::container::new_allocator<Value>>' being compiled [C:\Users\Lawrence Travis\source\repos\BoostNodeTree\build\BoostNodeTree.vcxproj]
with
[
Value=NodeTree::value
]
Here is the code and thanks in advance for any help!
Node.h:
#pragma once
#include <Windows.h>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
typedef bg::model::point<int, 2, bg::cs::cartesian> point_t;
typedef bg::model::box<point_t> box_t;
using namespace std;
class Node
{
public:
typedef Node value_type;
typedef const value_type& const_reference;
typedef std::size_t size_type;
Node() {}
~Node() {}
bool equal_to(const Node& that) {
const box_t& box1 = this->getBox();
const box_t& box2 = that.getBox();
return box1.min_corner().get<0>() == box2.min_corner().get<0>() &&
box1.min_corner().get<1>() == box2.min_corner().get<1>() &&
box1.max_corner().get<0>() == box2.max_corner().get<0>() &&
box1.max_corner().get<1>() == box2.max_corner().get<1>();
}
void setBox(RECT rect) {
box = box_t(point_t(rect.left, rect.top), point_t(rect.right, rect.bottom));
}
const box_t& getBox() const {
return box;
}
protected:
box_t box;
};
NodeTree.h:
#pragma once
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/index/rtree.hpp>
#include "Node.h"
namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;
namespace boost {
namespace geometry {
namespace index {
class node_indexable {
public:
typedef box_t result_type;
explicit node_indexable() {}
const result_type& operator()(const Node& node) {
return node.getBox();
}
};
template <typename NodeType>
class node_equal_to {
public:
typedef box_t result_type;
explicit node_equal_to() {}
bool equal_to(const NodeType& node1, const NodeType& node2) {
return node1.equal_to(node2);
}
};
} } }
class NodeTree {
public:
typedef Node value;
typedef bgi::linear<16> parameters;
typedef boost::geometry::index::node_indexable indexable_getter;
typedef boost::geometry::index::node_equal_to<Node> equal_to_getter;
bgi::rtree<value, parameters, indexable_getter, equal_to_getter> node_rtree;
NodeTree() {
parameters params;
indexable_getter ind;
equal_to_getter eq;
node_rtree = bgi::rtree<value, parameters, indexable_getter, equal_to_getter> (params, ind, eq);
}
void addNode(Node node) {
node_rtree.insert(node);
}
};
Class with main that triggers the link errors:
// BoostNodeTree.cpp :
//
#include <iostream>
#include "Node.h"
#include "NodeTree.h"
int main()
{
NodeTree nodeTree;
Node node;
RECT rect{ 10, 10, 100, 100 };
node.setBox(rect);
nodeTree.addNode(node);
}

Related

std::vector<structure>::iterator inside said structure causes "error: invalid use of incomplete type"

I have a C11 std::vector of structures. The structure contains an iterator to another structure of the same type, describing a tree.
Using a forward declaration doesn't work, because vector needs to have the full definition of the structure, but vector can't have the full definition of the structure until definition is complete.
#include <vector>
template <class Payload>
class Tree
{
public:
typedef struct _Node Node;
struct _Node
{
Payload t_payload;
//error: invalid use of incomplete type '_Value_type {aka struct _Node}'
std::vector<Node>::iterator pst_father;
};
std::vector<Node> gast_tree;
};
int main()
{
Tree<int> my_tree;
return 0;
}
in instantiation of 'void std::_Destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = _Node*]':
required from 'void std::_Destroy(_ForwardIterator, _ForwardIterator, std::allocator<_T2>&) [with _ForwardIterator = _Node*; _Tp = _Node]'
required from 'std::vector<_Tp, _Alloc>::~vector() [with _Tp = _Node; _Alloc = std::allocator<_Node>]'
required from here
error: invalid use of incomplete type '_Value_type {aka struct _Node}'
I want std::vector to serve as container for the Node structure, and I want Node(s) to link with each others to build a tree. It would be trivial using int indexes instead of iterators and resolving the reference later, but I'm trying to learn std::vector<>::iterators. This compiles just fine:
#include <vector>
template <class Payload>
class Tree
{
public:
typedef struct _Node
{
Payload t_payload;
//use a dumb index
int s32_father_index;
} Node;
std::vector<Node> gast_tree;
};
int main()
{
Tree<int> my_tree;
return 0;
}
I tried several ways, but I can't get the iterator to compile. Is it possible to have an iterator to an object inside the object?
SOLUTION
The keyword typedef is redundant in C++.
The keyword typename can be used in this scenario where the definition for the template is still incomplete. Details.
#include <vector>
template <class Payload>
class Tree
{
public:
struct Node
{
Payload t_payload;
typename std::vector<Node>::iterator pst_father;
};
std::vector<Node> gast_tree;
};
int main()
{
Tree<int> my_tree;
return 0;
}
SOLUTION
The keyword typedef is redundant in C++.
The keyword typename can be used in this scenario where the definition for the template is still incomplete.
#include <vector>
template <class Payload>
class Tree
{
public:
struct Node
{
Payload t_payload;
typename std::vector<Node>::iterator pst_father;
};
std::vector<Node> gast_tree;
};
int main()
{
Tree<int> my_tree;
return 0;
}

Error with calling a private constructor on macos

I am trying to port some C++ code that encodes the given data into base64 to macos. This is the a code sample from the same source which compiles and executes on godbolt but not on my mac:
#include <vector>
#include <string>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <boost/archive/iterators/insert_linebreaks.hpp>
using namespace boost::archive::iterators;
struct Encode
{
/// Sets the contents of an std::string to be used
/// as the input for the encoding operation.
/// \param a_Data The string to encode.
Encode(const std::string& a_Data);
/// Evaluates the expression and performs the base64 encoding.
/// \returns Base64 encoded string.
operator std::string() const;
private:
std::string Evaluate() const;
const char * m_Data;
size_t m_Size;
bool m_LineBreaks;
};
Encode::Encode(const std::string& a_Data)
: m_Data(a_Data.c_str())
, m_Size(a_Data.size())
, m_LineBreaks(false)
{
}
std::string Encode::Evaluate() const
{
typedef base64_from_binary<
transform_width<std::string::const_iterator,6,8>
> iterator;
typedef insert_linebreaks<iterator, 72> linebreak_iterator;
std::string base64;
if (m_LineBreaks) {
base64.assign(
linebreak_iterator(m_Data),
linebreak_iterator(m_Data + m_Size));
}
else {
base64.assign(
iterator(m_Data),
iterator(m_Data + m_Size));
}
return base64;
}
Encode::operator std::string() const
{
return Evaluate();
}
int main()
{
const std::string str64 = Encode("Hello World");
return 0;
}
I am compiling using g++:
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 10.0.0 (clang-1000.10.44.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
However, there is the following error on line iterator(m_Data),:
boost/archive/iterators/transform_width.hpp:112:17: error: calling a private constructor of class 'std::__1::__wrap_iter<const char *>'
super_t(Base(static_cast< T >(start))),
^
<build_path>/boost/archive/iterators/base64_from_binary.hpp:91:13: note: in instantiation of function template specialization 'boost::archive::iterators::transform_width<std::__1::__wrap_iter<const char *>, 6, 8,
char>::transform_width<const char *>' requested here
Base(static_cast< T >(start)),
^
<src_path>/utilsBase64.cc:105:13: note: in instantiation of function template specialization 'boost::archive::iterators::base64_from_binary<boost::archive::iterators::transform_width<std::__1::__wrap_iter<const char *>, 6, 8, char>, char>::base64_from_binary<const
char *>' requested here
iterator(m_Data),
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1420:31: note: declared private here
_LIBCPP_INLINE_VISIBILITY __wrap_iter(iterator_type __x) _NOEXCEPT_DEBUG : __i(__x) {}
^
I have attempted to use clang++ and tried setting -stdlib=libc++ or stdlib=libstdc++ with a similar result. All examples of using the base64_from_binary shown in the code are similar to the given code sample.
One additional detail about m_Data, it is a member variable declared as: const char * m_Data;
Could someone please explain how this can be resolved?
Modifying the code to the following helped fix the issue:
std::string Encode::Evaluate() const
{
typedef base64_from_binary<
transform_width<std::string::const_iterator,6,8>
> iterator;
typedef insert_linebreaks<iterator, 72> linebreak_iterator;
std::string data(m_Data, m_Size);
std::string base64;
if (m_LineBreaks) {
base64.assign(
linebreak_iterator(data.begin()),
linebreak_iterator(data.end()));
}
else {
base64.assign(
iterator(data.begin()),
iterator(data.end()));
}
return base64;
}

VStudio 2012 Create custom allocator for container of move-only type

I am trying to create an stl container of a move-only type that uses its own allocator in VStudio 2012.
The trouble is: it seems as though I have to provide a construct function for the allocator which in turn needs access to a public copy constructor on the contained type.
I either get:
error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
or
error C2039: 'construct' : is not a member of 'MyAllocator'
The same code works in clang so I suspect the problem is due to Microsoft but can anyone suggest a possible work around?
This is my code for minimal reproduction
#include <memory>
#include <vector>
using namespace std;
template< typename T>
struct MyAllocator
{
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef const value_type* const_pointer;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
template<class t_other>
struct rebind
{
typedef MyAllocator<t_other> other;
};
MyAllocator():m_id(0) {}
MyAllocator(int id):m_id(id){}
template <class T>
MyAllocator(const MyAllocator<T>& other)
:m_id(other.getId())
{
}
T* allocate(std::size_t n)
{
return reinterpret_cast<T*>(malloc(sizeof(T) * n));
}
void deallocate(T* p, std::size_t n)
{
free(p);
}
int getId() const{ return m_id;}
//Have to add these although should not be necessary
void construct(pointer mem, const_reference value)
{
std::_Construct(mem, value);
}
void destroy(pointer mem)
{
std::_Destroy(mem);
}
private:
int m_id;
};
template <class T1, class U>
bool operator==(const MyAllocator<T1>& lhs, const MyAllocator<U>& rhs)
{
return lhs.getId() == rhs.getId() ;
}
template <class T1, class U>
bool operator!=(const MyAllocator<T1>&, const MyAllocator<U>&)
{
return lhs.getId() != rhs.getId();
}
//define a move only type
typedef unique_ptr<uint32_t> MyIntPtr;
//define a container based on MyIntPtr and MyAllocator
typedef vector<MyIntPtr, MyAllocator<MyIntPtr> > MyVector;
int main(int argc, char* argv[])
{
MyAllocator<MyIntPtr> alloc1(1);
MyVector vec(alloc1);
uint32_t* rawPtr = new uint32_t;
*rawPtr = 18;
vec.emplace_back(rawPtr);
return 0;
}
The error you get is because you try to construct a std::unique_ptr from a constant reference to a std::unique_ptr of the same type - and there is no such constructor.
You can rework your construct method to take an an rvalue reference and then everything compiles nicely:
void construct(pointer mem, value_type&& value)
{
std::_Construct(mem, std::move(value));
}

std::bind and boost::bind tricks

#include <functional>
#include <boost/bind.hpp>
class A
{
public:
A(){}
~A(){}
template<typename _Handler>
void call_handler(_Handler handler)
{
handler();
}
};
class B
{
public:
template<typename _Handler>
void call_handler(_Handler handler)
{
}
template<typename _Handler>
void run(_Handler handler)
{
m_a.call_handler(boost::bind(&B::call_handler<_Handler>, this, handler));
//only can use boost::bind here
}
A m_a;
};
class Test
{
public:
void handler()
{
}
};
int main()
{
B b;
Test t;
b.run(boost::bind(&Test::handler,&t));//only can use std::bind here
}
this is my little test code above.
I am confused that I only can use bind in the specific order...see note above
if I change std::bind to boost::bind ,then compiler failed,vice versa.
tested with:
gcc 4.9.2 for cygwin with option:
g++ -std=c++11 -fdiagnostics-color=always -fdiagnostics-show-location=every-line -I"/cygdrive/e/opensource libs/boost" main.cpp
msvc 12.0(visual studio 2013 with update4) with default option.
gcc diagnostic messages:
In file included from /cygdrive/e/opensource
libs/boost/boost/bind.hpp:22:0,
from main.cpp:3: /cygdrive/e/opensource libs/boost/boost/bind/bind.hpp: In instantiation of ‘void
boost::_bi::list2::operator()(boost::_bi::type, F&, A&,
int) [with F = boost::_mfi::mf1,
boost::_bi::list1 > > >; A =
boost::_bi::list0; A1 = boost::_bi::value; A2 =
boost::_bi::bind_t,
boost::_bi::list1 > >]’:
/cygdrive/e/opensource libs/boost/boost/bind/bind.hpp:893:50:
required from ‘boost::_bi::bind_t::result_type
boost::_bi::bind_t::operator()() [with R = void; F =
boost::_mfi::mf1,
boost::_bi::list1 > > >; L =
boost::_bi::list2, boost::_bi::bind_t,
boost::_bi::list1 > > >;
boost::_bi::bind_t::result_type = void]’ main.cpp:12:11:
required from ‘void A::call_handler(_Handler) [with _Handler =
boost::_bi::bind_t,
boost::_bi::list1 > > >,
boost::_bi::list2, boost::_bi::bind_t,
boost::_bi::list1 > > > >]’ main.cpp:27:3:
required from ‘void B::run(_Handler) [with _Handler =
boost::_bi::bind_t,
boost::_bi::list1 > >]’ main.cpp:44:38:
required from here /cygdrive/e/opensource
libs/boost/boost/bind/bind.hpp:313:34: error: invalid use of void
expression
unwrapper::unwrap(f, 0)(a[base_type::a1_], a[base_type::a2_]);
^
msvc 12.0 diagnostic messages:
1>------ Build started: Project: scince_x32, Configuration: Debug
Win32 ------ 1> main.cpp 1>e:\opensource
libs\boost\boost\bind\bind.hpp(313): error C2664: 'void
boost::_mfi::mf1::operator ()(T *,A1) const' : cannot
convert argument 2 from 'void' to
'boost::_bi::bind_t,boost::_bi::list1>>'
1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B 1> , A1=boost::_bi::bind_t,boost::_bi::list1>> 1> ] 1> and 1> [ 1> T=Test * 1> ] 1> Expressions of type void cannot be
converted to other types 1> e:\opensource
libs\boost\boost\bind\bind.hpp(893) : see reference to function
template instantiation 'void
boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>::operator
()(boost::_bi::type,F &,A &,int)' being
compiled 1> with 1> [ 1> T=B * 1> ,
F=boost::_mfi::mf1,boost::_bi::list1>>> 1> , A=boost::_bi::list0 1> ] 1> e:\opensource libs\boost\boost\bind\bind.hpp(893) : see reference to
function template instantiation 'void
boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>::operator
()(boost::_bi::type,F &,A &,int)' being
compiled 1> with 1> [ 1> T=B * 1> ,
F=boost::_mfi::mf1,boost::_bi::list1>>> 1> , A=boost::_bi::list0 1> ] 1> e:\opensource libs\boost\boost\bind\bind.hpp(891) : while compiling
class template member function 'void
boost::_bi::bind_t,boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>>::operator
()(void)' 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B * 1> ] 1> e:\c++program\scince_x32\scince_x32\main.cpp(12) : see reference to
function template instantiation 'void
boost::_bi::bind_t,boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>>::operator
()(void)' being compiled 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B * 1> ] 1> e:\c++program\scince_x32\scince_x32\main.cpp(27) : see reference to
class template instantiation
'boost::_bi::bind_t,boost::_bi::list2,boost::_bi::bind_t,boost::_bi::list1>>>>'
being compiled 1> with 1> [ 1>
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> , T=B * 1> ] 1> e:\c++program\scince_x32\scince_x32\main.cpp(44) : see reference to
function template instantiation 'void
B::run,boost::_bi::list1>>>(_Handler)'
being compiled 1> with 1> [ 1> T=Test *
1> ,
_Handler=boost::_bi::bind_t,boost::_bi::list1>> 1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
You're (indirectly) binding a bind-expression that binds as an argument another bind-expression.
In both Boost and standard library you need to protect the inner bind-expression so that the placeholders/bindings don't mix and conflict.
So it would begin with
m_a.call_handler(boost::bind(&B::call_handler<_Handler>, this, boost::protect(handler)));
Except, the protect(handler) wraps the type and it's no longer _Handler, so you need something else for &B::call_handler<???>. In my experience, the easiest by far, is to use a polymorphic function object:
struct handler_caller_f {
typedef void result_type;
template <typename H> void operator()(B* /*this_*/, H/* handler*/) const {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
template <typename H> void run(H handler) {
m_a.call_handler(boost::bind(handler_caller_f(), this, boost::protect(handler)););
}
As you can see the function object replaces B::call_handler and removes the problem by deducing the type of handler again.
Here's a cleaned up version:
Using Boost
Live On Coliru
#include <boost/bind.hpp>
#include <boost/bind/protect.hpp>
#include <iostream>
struct A {
template <typename H> void call_handler(H handler) {
handler();
}
};
struct B {
struct handler_caller_f {
typedef void result_type;
template <typename H> void operator()(B* this_, H handler) const {
handler();
}
};
template <typename H> void run(H handler) {
m_a.call_handler(boost::bind(handler_caller_f(), this, boost::protect(handler)));
}
A m_a;
};
struct Test {
void handler() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
int main() {
Test t;
B b;
b.run(boost::bind(&Test::handler, &t));
}
Prints
void Test::handler()
Using std::bind
Standard library doesn't have protect, but it's easy to add:
Live On Coliru
#include <functional>
#include <iostream>
namespace std_ex { // http://stackoverflow.com/questions/18519087/why-is-there-no-stdprotect
template <typename T> struct protect_wrapper : T {
protect_wrapper(const T &t) : T(t) {}
protect_wrapper(T &&t) : T(std::move(t)) {}
};
template <typename T>
typename std::enable_if<!std::is_bind_expression<typename std::decay<T>::type>::value, T && >::type protect(T &&t) {
return std::forward<T>(t);
}
template <typename T>
typename std::enable_if<std::is_bind_expression<typename std::decay<T>::type>::value,
protect_wrapper<typename std::decay<T>::type> >::type
protect(T &&t) {
return protect_wrapper<typename std::decay<T>::type>(std::forward<T>(t));
}
}
struct A {
template <typename H> void call_handler(H handler) { handler(); }
};
struct B {
struct handler_caller_f {
typedef void result_type;
template <typename H> void operator()(B *this_, H handler) const { handler(); }
};
template <typename H> void run(H handler) {
m_a.call_handler(std::bind(handler_caller_f(), this, std_ex::protect(handler)));
}
A m_a;
};
struct Test {
void handler() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
int main() {
Test t;
B b;
b.run(std::bind(&Test::handler, &t));
}

Is it possible for C++ to write a template that takes a Container as parameter?

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

Resources