Composing a set of container classes and accessing them from a base - c++

I'm trying to use boost::mpl::inherit_linearly to compose a container class using types provided by the user:
#include <typeinfo>
#include <iostream>
#include <vector>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/vector.hpp>
namespace mpl = ::boost::mpl;
//////////////////////////////////////////////
// Create the container by chaining vectors
//////////////////////////////////////////////
struct Base {};
// Types provided by the user
typedef mpl::vector<int, char, double>::type myTypes;
typedef mpl::inherit_linearly<
myTypes,
mpl::inherit<mpl::_1, std::vector<mpl::_2> >,
Base
>::type InheritedContainer;
// Function for accessing containers
template <typename T>
inline std::vector<T>& get_container(Base& c) {
return static_cast<std::vector<T>& >(c);
}
// Some functions that manipulate the containers
// NB: These functions only know about the Base and the types
// they want to access
void my_int_func(Base& b) {
get_container<int>(b).push_back(42);
}
void my_char_func(Base& b) {
get_container<char>(b).push_back('c');
}
int main() {
InheritedContainer container;
Base& bref = container;
my_int_func(bref);
std::cout << "Int: " << get_container<int>(bref).back() << std::endl;
my_char_func(bref);
std::cout << "Char: " << get_container<char>(bref).back() << std::endl;
return 0;
}
The compile error I get is:
question.cpp: In function ‘std::vector<T, std::allocator<_CharT> >& get_container(Base&) [with T = int]’:
question.cpp:40: instantiated from here
question.cpp:31: error: invalid static_cast from type ‘Base’ to type ‘std::vector<int, std::allocator<int> >&’
question.cpp: In function ‘std::vector<T, std::allocator<_CharT> >& get_container(Base&) [with T = char]’:
question.cpp:44: instantiated from here
question.cpp:31: error: invalid static_cast from type ‘Base’ to type ‘std::vector<char, std::allocator<char> >&’
Shouldn't Base be a base of whatever type is produced by inherit_linearly? And if so, shouldn't a vector<int> and the other vectors show up in the type hierarchy for static_cast to pull out?
Is there any other way to get this functionality?

I think Base is a base class of InheritedContainer, but not of
std::vector<int>.
As we know, std::vector isn't defined as the following:
class vector : Base {...
You may expect the following inheritance:
class InheritedContainer : Base, std::vector<int>, ... {...
However, in this case, the cast from Base to vector<int> is a cross-cast,
so this cannot be done with static_cast.
As you may know, the following is allowed:
InheritedContainer container;
Base& bref = container;
InheritedContainer& iref = static_cast<InheritedContainer&>(bref);
std::vector<int>& vi = iref;
std::vector<char>& vc = iref;
If you can prepare get_container, my_int_func and my_char_func, probably the types
to which the std::vector will be specialized are known beforehand.
If so, I suppose it is pertinent to hold InheritedContainer& instead of Base&
all along.
If you have to cast Base to vector<T>, probably
RTTI(for example adding virtual function to Base) and dynamic_cast will enable
the cast.

Related

Creating a vector of the type of std::any

Consider the following example
#include <iostream>
#include <any>
#include <vector>
#include <map>
#include <typeinfo>
typedef enum TYPE{
INT8=0,
INT16=1,
INT32=2
} TYPE;
int main()
{
std::map<TYPE, std::any> myMap;
myMap[TYPE::INT8] = (int8_t)0;
myMap[TYPE::INT16] = (int16_t)0;
myMap[TYPE::INT32] = (int32_t)0;
std::vector<decltype(myMap[TYPE::INT8])> vec;
}
I have a map in this example, going from some enum to std::any. I actually need a flexible data structure that can map from a specific type (enum TYPE in this case), to multiple data types (different types of int), hence the use of std::any.
Going ahead, I would like to ascertain the type of value given for the key and construct a vector with it. I tried the above code, and it runs into a compilation error because decltype will return std::any(correctly so).
I would want to extract the "true type" from the std::any and create that type of vector. How would I achieve that.
A small snippet of the compilation error is as follows -
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/new_allocator.h:63:26: error: forming pointer to reference type 'std::any&'
63 | typedef _Tp* pointer;
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/new_allocator.h:112:7: error: forming pointer to reference type 'std::any&'
112 | allocate(size_type __n, const void* = static_cast<const void*>(0))
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/stl_vector.h:1293:7: error: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any&; _Alloc = std::allocator<std::any&>; value_type = std::any&]' cannot be overloaded with 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any&; _Alloc = std::allocator<std::any&>; value_type = std::any&]'
1293 | push_back(value_type&& __x)
TIA
As suggested in the comments by #Ted Lyngmo, I think std::variant serves you better. Especially with C++-20's templated lambdas, the std::visit function can work wonders with these to get around the awkwardness of dealing with type enums and the like.
Note that you can not get around the runtime type detection. In any case, here is an example of how it can work.
#include <cstdint>
#include <iostream>
#include <variant>
#include <vector>
using VariantScalar = std::variant<
std::int8_t, std::int16_t, std::int32_t>;
using VariantVector = std::variant<
std::vector<std::int8_t>,
std::vector<std::int16_t>,
std::vector<std::int32_t>>;
VariantVector fill_vector(VariantScalar scalar, std::size_t n)
{
auto make_vector = [n]<class IntType>(IntType v) -> VariantVector {
return std::vector<IntType>(n, v);
};
return std::visit(make_vector, scalar);
}
void print_vector(const VariantVector& vec)
{
std::visit([]<class T>(const std::vector<T>& vec) {
for(const T& s: vec)
std::cout << s << ' ';
std::cout << '\n';
}, vec);
}
int main()
{
VariantScalar s(std::int8_t(1));
VariantVector vec = fill_vector(s, 5);
print_vector(vec);
}
Assuming you have the following enum definition:
enum class TYPE{
INT8=0,
INT16=1,
INT32=2
};
Then you can define a helper:
template <TYPE>
struct my_type {}; // Base case
template <>
struct my_type<TYPE::INT8> {
using type = int8_t;
};
template <>
struct my_type<TYPE::INT16> {
using type = int16_t;
};
template <>
struct my_type<TYPE::INT32> {
using type = int32_t;
};
template <TYPE t>
using my_type = typename my_type<t>::type;
That you can use for your vector
std::vector<my_type<TYPE::INT8>> vec;

Issue with typedefining iterators with in the class declaration [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I'm working on a legacy code which is poorly written and only compiles within Microsoft Visual Studios and with Visual C++ compiler. GCC, G++, or Clang all fails to compile the code due to build time errors. I have narrowed down the issue to the following class declaration which instantiate a STL container of the class type within the class declaration:
#include <map>
#include <set>
#include <iomanip>
#include <string>
#include <cmath>
#include <iterator>
#include <unordered_map>
#include <bits/unique_ptr.h>
#define HASH_MAP unordered_map
using namespace std;
namespace XYZ {
class abc {
public:
typedef HASH_MAP<double, abc> MAP; // This is the problem ?
typedef MAP::iterator Iterator;
typedef MAP::const_iterator ConstIterator;
typedef pair<double, abc> Pair;
bool less(abc::Pair& a, abc::Pair& b) { return a.first < b.first; }
public:
abc() {}
~abc() { }
};
}
I want to know what is the best way to refactor this code segment while preserving the structure of the code. For example, I was trying to make the MAP definition with a pointer type (i.e., typedef HASH_MAP<double, XYZ*> MAP) This change worked with GCC compiler however, since, I'm changing to pointer type I would have to dig deep in to the code base and modify most of the code base as this class is playing a key role in other dependent code.
So I was wondering if there is an alternative to fix this issue which would not require significant change to the original code base. I was thinking in the line of making a friend class similar.
Following is the compiler error:
In file included from /usr/include/c++/4.8/bits/stl_algobase.h:64:0,
from /usr/include/c++/4.8/bits/stl_tree.h:61,
from /usr/include/c++/4.8/map:60,
from /home/user/work/wxy.h:4,
from /home/user/work/abc.h:4,
from /home/user/work/abc.cpp:1:
/usr/include/c++/4.8/bits/stl_pair.h: In instantiation of ‘struct std::pair<const double, XYZ::abc>’:
/usr/include/c++/4.8/type_traits:615:28: required from ‘struct std::__is_destructible_impl<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/type_traits:637:12: required from ‘struct std::__is_destructible_safe<std::pair<const double, XYZ::abc>, false, false>’
/usr/include/c++/4.8/type_traits:652:12: required from ‘struct std::is_destructible<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/type_traits:116:12: required from ‘struct std::__and_<std::is_destructible<std::pair<const double, XYZ::abc> >, std::__is_direct_constructible_impl<std::pair<const double, XYZ::abc>, const std::pair<const double, XYZ::abc>&> >’
/usr/include/c++/4.8/type_traits:817:12: required from ‘struct std::__is_direct_constructible_new_safe<std::pair<const double, XYZ::abc>, const std::pair<const double, XYZ::abc>&>’
/usr/include/c++/4.8/type_traits:895:12: [ skipping 4 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/c++/4.8/type_traits:968:12: required from ‘struct std::__is_copy_constructible_impl<std::pair<const double, XYZ::abc>, false>’
/usr/include/c++/4.8/type_traits:974:12: required from ‘struct std::is_copy_constructible<std::pair<const double, XYZ::abc> >’
/usr/include/c++/4.8/bits/alloc_traits.h:540:12: required from ‘struct std::__is_copy_insertable<std::allocator<std::pair<const double, XYZ::abc> > >’
/usr/include/c++/4.8/bits/alloc_traits.h:560:63: required by substitution of ‘template<class _Alloc> using __check_copy_constructible = std::__allow_copy_cons<std::__is_copy_insertable<_Alloc>::value> [with _Alloc = std::allocator<std::pair<const double, XYZ::abc> >]’
/usr/include/c++/4.8/bits/unordered_map.h:97:11: required from ‘class std::unordered_map<double, XYZ::abc>’
/home/user/work/abc.h:27:20: required from here
/usr/include/c++/4.8/bits/stl_pair.h:102:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
_T2 second; /// #c second is a copy of the second object
^
In file included from /home/user/work/abc.cpp:1:0:
/home/user/work/abc.h:24:11: error: forward declaration of ‘class XYZ::abc’
class abc {
^
The issue is (for whatever reasons -- hell, it's just a typedef!) with the Iterators. If you move those out of your class code, so that the class definition is complete at the point of their definition, it compiles (with g++). Perhaps one then should rename Iterator to MAP_Iterator or such. I can imagine that the changes required in client code then are manageable
Edit: After your comment I had the idea to put the iterator typedefs in a class called abc (the original class name) to retain source compatibility with the client code. The actual class definition is moved to a base class which the client code doesn't need to use explicitly. The map holds objects of the base class, which involves slicing and back conversion when storing or retrieving true abcs. It would probably not be possible to hold abc references to values in the map, but the simple value-based example below (using the iterators) works. There are a few comments in the source code.
#include <unordered_map>
#include <iostream>
using namespace std;
// The "original" abc
class abcBase
{
public:
typedef unordered_map<double, abcBase> MAP; // This is the problem ?
typedef pair<double, abcBase> Pair;
bool less(abcBase::Pair& a, abcBase::Pair& b) { return a.first < b.first; }
string tag;
public:
abcBase(string t): tag(t){}
abcBase() = default;
~abcBase() { }
};
// The abc presented to the users for source compatibility.
// There is a conversion
// from base to derived via constructor.
//
// Note that a MAP
// holds abcBase objects, not abc objects! We need to convert them
// when we store and when we read.
// Conversion derived -> base is via slicing
// (which does not do any harm as long as we do not define
// data members in derived).
class abc: public abcBase
{
public:
// conversion constructor
abc(const abcBase &b): abcBase(b){}
abc(string t): abcBase(t){}
abc() = default;
typedef MAP::iterator Iterator;
typedef MAP::const_iterator ConstIterator;
};
int main()
{
abc::MAP m;
abc a("a"),b("b"),c("c");
m[1.0] = a; // conversion abc -> abcBase ...
m[2.0] = b;
m[3.0] = c;
a = m[1.0]; // conversion abcBase -> abc via ctor
for( abc::Iterator i = m.begin(); i != m.end(); ++i)
{
cout << "key: " << i->first << ", val: " << i->second.tag << endl;
}
return 0;
}
You're using std::unordered_map so you're using C++11.
So I suppose that the use of std::unique_ptr (typedef HASH_MAP<double, std::unique_ptr<XYZ>> MAP instead of typedef HASH_MAP<double, XYZ*> MAP) can help you a lot in reducing the following modifications.
By example, the following code compile
#include <utility>
#include <memory>
#include <unordered_map>
using namespace std;
#define HASH_MAP unordered_map
namespace abc {
class XYZ {
private:
int _test;
public:
typedef HASH_MAP<double, std::unique_ptr<XYZ>> MAP; // This is the problem ?
typedef MAP::iterator Iterator;
typedef MAP::const_iterator ConstIterator;
typedef pair<double, std::unique_ptr<XYZ>> Pair;
bool less(XYZ::Pair& a, XYZ::Pair& b) { return a.first < b.first; }
public:
XYZ():_test(0) {}
~XYZ() { }
// Some function definitions
};
};
int main ()
{
return 0;
}
Obviously I'm supposing your namespace is abc and your class name is XYZ; you can't put a namespace in a map.
p.s.: sorry for my bad English.

Custom class pointer as unordered map key

I'm trying to implement unordered_map with pointer to custom class as key and integer as value.
I thought pointer is just an address, so I wouldn't have to create comparison template for unordered_map, since map would compare between addresses. But I get compile error.
My code is as follow for simple testing. Can anyone help me to fix what have I done wrong?
#include <cstdlib>
#include <unordered_map>
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(int id){m_id = id;};
void PrintThis(){cout << " This is test " << endl;};
int m_id;
};
class Test{
public:
unordered_map<MyClass* mc, int test> mapTest;
};
int main(){
MyClass* mc1 = new MyClass(1);
MyClass* mc2 = new MyClass(2);
Test* tt1 = new Test();
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
tt1->mapTest.insert(make_pair<MyClass*, int>(mc2, 20));
auto search = tt1->find(mc1);
if(search != tt1->end()) {
search->first->PrintThis();
}else{
cout << "not Found " << endl;
}
}
Error message is as follow
./main.cpp:17:44: error: wrong number of template arguments (1, should be 5)
unordered_map<MyClass* mc, int test> mapTest;
^
In file included from /usr/include/c++/4.8/unordered_map:48:0,
from ./main.cpp:2:
/usr/include/c++/4.8/bits/unordered_map.h:97:11: error: provided for 'template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class std::unordered_map'
class unordered_map : __check_copy_constructible<_Alloc>
^
./main.cpp: In function 'int main()':
./main.cpp:26:18: error: request for member 'insert' in 'tt1->Test::mapTest', which is of non-class type 'int'
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
I think I can manage line 26 error, if line 17 gets fixed...
Thanks in advance!
I tried your code and found 3 problems:
Declaration of map: it should read std::unordered_map<MyClass*, int>
call of undefined functions (tt1->find/tt1->end, should read tt1->testMap.XXX)
Call of make_pair doesn't require template arguments. The compiler will infer them. This actually causes a problem, as the compiler tries to call make_pair(MyClass *&&, int &&). If I omit the template arguments, it works (make_pair(mc1, 10))
As for point 3:
make_pair is declared as follows in C++11 (C++14 just adds constexpr) (cppreference):
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
For template argument deduction, the follwing rule applies (cf. cppreference)
4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward)
(emphasis mine)
So the compiler will infer:
std::make_pair<MyClass *&, int>(MyClass *&, int &&);
where MyClass *& can bind to your actual argument.
If you directly specify the template types, the compiler will stick to
std::make_pair<MyClass *, int>(MyClass *&&, int &&).
As your argument is a lvalue, it cannot be converted to a rvalue-reference, and compilation fails
Your declaration of unordered_map<MyClass* mc, int test> mapTest; is invalid syntax. It should be unordered_map<MyClass*, int> mapTest.
Also remove the template parameters from your make_pair calls and change tt1->find to ttl->mapTest.find() and ttl->end() to ttl->mapTest.end().

C++ templated functor in lambda expression

This first piece has been solved by Eric's comments below but has led onto a secondary issue that I describe after the horizontal rule. Thanks Eric!
I'm trying to pass a functor that is a templated class to the create_thread method of boost thread_group class along with two parameters to the functor. However I can't seem to get beyond my current compile error. With the below code:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>
using namespace boost::lambda;
using namespace std;
namespace bl = boost::lambda;
template<typename ftor, typename data>
class Foo
{
public:
explicit Foo()
{
}
void doFtor ()
{
_threads.create_thread(bind(&Foo<ftor, data>::_ftor, _list.begin(), _list.end()));
//_threads.create_thread(bind(_ftor, _list.begin(), _list.end()));
_threads.join_all();
}
private:
boost::thread_group _threads;
ftor _ftor;
vector<data> _list;
};
template<typename data>
class Ftor
{
public:
//template <class Args> struct sig { typedef void type; }
explicit Ftor () {}
void operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
{
for_each(startItr, endItr, cout << bl::_1 << constant("."));
}
}
I also tried typedef-ing 'type' as I thought my problem might have something to do with the Sig Template as the functor itself is templated.
The error I am getting is:
error: no matching function for call to ‘boost::lambda::function_adaptor<Ftor<int> Foo<Ftor<int>, int>::*>::apply(Ftor<int> Foo<Ftor<int>, int>::* const&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int>> >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’
with a bunch of preamble beforehand.
Thanks in advance for any help!
Okay I've modified the code taking in Eric's suggestions below resulting in the following code:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>
using namespace boost::lambda;
using namespace std;
namespace bl = boost::lambda;
template<typename ftor, typename data>
class Foo
{
public:
explicit Foo()
{
}
void doFtor ()
{
_threads.create_thread(bl::bind(boost::ref(_ftor), _list.begin(), _list.end()));
_threads.join_all();
}
private:
boost::thread_group _threads;
ftor _ftor;
vector<data> _list;
};
template<typename data>
class Ftor
{
public:
typedef void result_type;
explicit Ftor () {}
result_type operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
{
for_each(startItr, endItr, cout << bl::_1 << constant("."));
return ;
}
};
However this results in another compile error:
/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: no match for call to ‘(Ftor<int>) (const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’
ftor.h:41: note: candidates are: void Ftor<data>::operator()(typename std::vector<data, std::allocator<_CharT> >::iterator&, typename std::vector<data, std::allocator<_CharT> >::iterator&) [with data = int]
/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: return-statement with a value, in function returning 'void'
It seems having defined void as a result_type it is now expecting the operator() to return something. I tried returning result_type from within the function but this also generated errors. Any ideas?
Sig (or in your case, simply typedef void result_type; is necessary.
IIRC, lambda::bind makes const copies of its arguments.
There is thus a problem with functors with non-const operator(). This is solved by making Ftor::operator()const or by wrapping (in doFtor()), _ftor with boost::ref
There is a similar problem with the iterators. Wrapping in boost::ref here won't work directly because it would end up using a reference to a temporary. The simpler solution is to modify Ftor::operator() to take its arguments by copy.
The simplest is thus to modify Ftor so that its operator() is const and it takes its arguments by copy:
void operator() (typename vector<data>::iterator startItr, typename vector<data>::iterator endItr)const
If you really can't make Ftor::operator() const, you could modify doFtor() as follows (but it is still necessary to make Ftor::operator() take its arguments by copy):
_threads.create_thread(bind(boost::ref(_ftor), _list.begin(), _list.end()));

c++ template casting with derived classes

#include <vector>
struct A {int a;};
struct B : public A {char b;};
int main()
{
B b;
typedef std::pair<A*, A*> MyPair;
std::vector<MyPair> v;
v.push_back(std::make_pair(&b, &b)); //compiler error should be here(pair<B*,B*>)
return 0;
}
I don't understand why this compiles (maybe somebody can kindly provide detailed explanation? Is it something related to name look-up?
Btw, on Solaris, SunStudio12 it doesn't compile: error : formal argument x of type const std::pair<A*, A*> & in call to std::vector<std::pair<A*,A*> >::push_back(const std::pair<A*, A*> & ) is being passed std::pair<B*, B*>
std::pair has a constructor template:
template<class U, class V> pair(const pair<U, V> &p);
"Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed." (C++03, 20.2.2/4)
Conversion from a derived class pointer to a base class pointer is implicit.
Because B is derived from A, the vector v will contain pointers to base class structures of the object b. therefore, you could access the members of A, i.e.
std::cout << v[0].first->a;
EDIT:
My mistake, as pointed out below, you can still cast to pointers of type B since the vector is of pointers, not objects, so no object slicing has occurred.
A call such as
std::cout << v[0].first->b;
will not compile since the elements in the vector are base class pointers and cannot point to derived class members without a cast, i.e.
std::cout << static_cast<B*>(v[0].first)->b;
Also note that a dynamic cast, as in
std::cout << dynamic_cast<B*>(v[0].first)->b;
will not compile with the following error in gcc:
cast.cpp:14: error: cannot dynamic_cast ‘v.std::vector<_Tp, _Alloc>::operator[] [with _Tp = std::pair<A*, A*>, _Alloc = std::allocator<std::pair<A*, A*> >](0u)->std::pair<A*, A*>::first’ (of type struct A*’) to type struct B*’ (source type is not polymorphic)