C++ Dynamically assign std::map comparator - c++

So I have two classes containing std::map members with effectively identical functionality except that the ordering of one map is std::less and the other std::greater.
If I create an abstract parent class and declare a single map member, is there any way to dynamically assign the comparator for this member in the derived class constructors? That way the functionality can obviously all reside in the parent class.

You can't change the comparator after the fact. But you can use the same comparator class and get either "greater" or "less" at the time of construction. You just need a stateful comparator:
struct my_compare {
enum compare_type { less, greater };
explicit my_compare(compare_type t) : m_type(t) {}
template<class T, class U>
bool operator()(const T& t, const U& u) const {
if(m_type == less) { return t < u; }
else { return t > u; }
}
compare_type m_type;
};
Then you can do
std::map<int, int, my_compare> less_map((my_compare(my_compare::less)));
std::map<int, int, my_compare> greater_map((my_compare(my_compare::greater)));
The extra pair of parentheses is because it would otherwise be the most vexing parse , even though a function parameter declaration cannot have a qualified name. In C++11, list-initialization (my_compare{mycompare::less}) can be used instead.
For your specific design, an implementation might look like
class A {
protected:
explicit A(my_compare::compare_type ct) : my_map(my_compare(ct)) {}
std::map<int, int, my_compare> my_map;
};
class B_less : public A{
public:
B_less() : A(my_compare::less) {}
};

No. The comparison function is used to generate the actual data structure -- changing the comparison function would require rebuilding the structure from scratch.
That said, if all you want to do is iterate the structure in reverse order, map is a reversible container, so you can just loop over the structure normally using the reverse iterators.

You can do what you want to do by creating a custom functor class that uses less or greater depending on some state. Here's an example:
#include <iostream>
#include <string>
#include <map>
struct MyCompare
{
MyCompare(bool useLess) : useLess_(useLess) {}
bool operator()(int lhs, int rhs)
{
if ( useLess_ )
{
return (lhs < rhs);
}
else
{
return (lhs > rhs);
}
}
bool useLess_;
};
int main(int argc, char** argv)
{
std::map<int, std::string, MyCompare> myMap1(MyCompare(true));
std::map<int, std::string, MyCompare> myMap2(MyCompare(false));
myMap1[1] = "abcd";
myMap1[2] = "lmnop";
myMap1[3] = "xyz";
myMap2[1] = "abcd";
myMap2[2] = "lmnop";
myMap2[3] = "xyz";
std::cout << "Map 1: " << std::endl; for ( auto const& v : myMap1 )
{
std::cout << "Key: " << v.first << ", Value: " << v.second << std::endl;
}
std::cout << "Map 2: " << std::endl;
for ( auto const& v : myMap2 )
{
std::cout << "Key: " << v.first << ", Value: " << v.second << std::endl;
}
return 0;
}
Output:
Map 1:
Key: 1, Value: abcd
Key: 2, Value: lmnop
Key: 3, Value: xyz
Map 2:
Key: 3, Value: xyz
Key: 2, Value: lmnop
Key: 1, Value: abcd
In your case, you can pass a flag from the child class to the parent class indicating what value to use to create the compare functor.

Related

Why the constructor of the map allow us to pass a comparator object in the parameter?

#include <map>
#include <string>
class A_comparator
{
public:
template <typename T>
bool operator()(const T& lhs, const T& rhs) const
{
// Defines a custom ordering for the elements in the map
// ...
}
};
int main()
{
A_comparator camp1;
std::map<int, std::string, A_comparator> map1(camp1);
map1[1] = "one";
map1[2] = "two";
map1[3] = "three";
A_comparator camp2;
std::map<int, std::string, A_comparator> map2(camp2);
map2[1] = "one";
map2[2] = "two";
map2[3] = "three";
// Prints elements in custom order based on their keys
for (const auto& [key, value] : map1)
std::cout << key << ": " << value << std::endl;
std::cout << std::endl;
for (const auto& [key, value] : map2)
std::cout << key << ": " << value << std::endl;
return 0;
}
In this example, two std::map objects, map1 and map2, are created and each is initialized with a different A_comparator object, camp1 and camp2, respectively. Both map1 and map2 use the A_comparator comparator to compare their keys, so they will both be ordered according to the custom ordering defined by the A_comparator class.
using different A_comparator objects with std::map does not affect the behavior of the map.
So why the definition of the map could not be simply like:
....
typedef Compare key_compare;
key_compare _comp;
explicit map (): _comp(key_compare(){}
....
the code above will produce the same behavior.
I think the key point here to understand is that the type of the comparator is part of the type of the map. Hence, if you use a different type of comparator, it is a different type of map.
Now, sometimes you want to have the same type of map, but still use different ways of comparing the keys for different instances of the map. You cannot use a different type of comparator for two instances of the same type of map.
But, you can use different instances of the same type of comparator with different instances of the same type of map.
For example:
#include <iostream>
#include <map>
struct cmp {
bool flipped = false;
bool operator()(const int& a,const int& b) const {
if (flipped) return b < a;
return a < b;
}
};
void populate_and_print(std::map<int,int,cmp>& m){
m[1] = 42;
m[2] = 102;
for (const auto& e : m) { std::cout << e.first << "\n"; }
}
int main() {
auto m = std::map<int,int,cmp>(cmp{});
populate_and_print(m);
auto n = std::map<int,int,cmp>(cmp{true});
populate_and_print(n);
}
Notice how populate_and_print does not need to be a template. m and n are both of the same type std::map<int,int,cmp>. The two instances of the comparator are of the same type cmp, but they compare differently.
If you would write a different comparator cmp2 then std::map<int,int,cmp> and std::map<int,int,cmp2> are two different types.
Also, if you use a plain function as the comparator, you must provide an instance. Consider:
bool cmp1(const int& a,const int& b) { return a < b; }
bool cmp2(const int& a,const int& b) { return b < a; }
The type of both functions is bool (const int&,const int&). You cannot default construct a free function. You need to pass a function pointer to the map's constructor.

Iterate throught n-dimensional vector c++

I wanted to write my own code to iterate over an n dimensional vector (where the dimension is known). Here is the code:
void printing(const auto& i, const int dimension){
int k= dimension;
for(const auto& j: i){
if(k>1){
cout<<"k: "<<k<<endl;
printing(j, --k);
}
else{
//start printing
cout<<setw(3);
cout<<j; //not quite sure about this line
}
cout<<'\n';
}
}
I get an error:
main.cpp:21:5: error: ‘begin’ was not declared in this scope
for(const auto& j: i){
^~~
Could someone help me to correct it or give me a better way to print the vector?
Thanks in advance for your time.
If the dimensions are known at compile-time, this can be solved easily with a template that takes dimensions as the non-type argument.
template <std::size_t Dimensions>
void printing(const auto& i){
if constexpr (Dimensions != 0) {
for(const auto& j: i){
// I'm not sure if it is intentional to print 'k' each iteration,
// but this is kept for consistency with the question
cout<<"k: " << Dimensions << endl;
printing<Dimensions - 1u>(j);
}
} else {
cout << setw(3);
cout << j;
cout << '\n';
}
}
The use would be, for a 2d vector:
printing<2>(vec);
Live Example
However, if you always know that const auto& i will be a std::vector type, you can potentially solve this even easier by just not using auto arguments at all, and instead use template matching:
// called only for the vector values
template <typename T>
void printing(const std::vector<T>& i){
for(const auto& j: i){
// possibly compute 'k' to print -- see below
printing(j);
}
}
// Only called for non-vector values
template <typename T>
void printing(const T& v) {
cout << setw(3);
cout << v;
cout << '\n';
}
Live Example
To compute the "dimension" of the vector, you can write a recursive type-trait for that:
#include <type_traits> // std::integral_constant
// Base case: return the count
template <std::size_t Count, typename T>
struct vector_dimension_impl
: std::integral_constant<std::size_t, Count> {};
// Recursive case: add 1 to the count, and check inner type
template <std::size_t Count, typename T, typename Allocator>
struct vector_dimension_impl<Count, std::vector<T,Allocator>>
: vector_dimension_impl<Count + 1u, T> {};
// Dispatcher
template <typename T>
struct vector_dimension : vector_dimension_impl<0u, T> {};
// Convenience access
template <typename T>
inline constexpr auto vector_dimension_v = vector_dimension<T>::value;
// Simple tests:
static_assert(vector_dimension_v<std::vector<int>> == 1u);
static_assert(vector_dimension_v<std::vector<std::vector<int>>> == 2u);
static_assert(vector_dimension_v<std::vector<std::vector<std::vector<int>>>> == 3u);
Live Example
With the above recursive trait, you can get the "dimension" of each templated vector type, without requiring the user to pass in the value at all.
If you still wanted to print k: each time, you can use the above simply with:
cout << "k: " << vector_dimension_v<T> << endl;
This only works if the type is known to be a vector -- but it could be written using concepts to work with anything following the abstract definition of something like a vector as well.
If you want this to work with any range-like type, then you could replace the vector-overload with a requires(std::ranges::range<T>) instead, and change the template-specializations for finding the dimension to also use the same. I won't pollute the answer with all this code since it's largely the same as above -- but I'll link to it in action below:
Live Example
I have made a function that can print any n-dimensional iterable container:
template<typename Object, typename Iterable>
void Print(
const Iterable& iterable,
const string& separatorDimensions = "\n",
const function<void(const Object&)>& funcPrintElem = [] (const Object& obj) {
static_assert(
is_arithmetic_v<Object> || is_same_v<remove_const_t<remove_pointer_t<Object>>, char>,
R"(The object from the innermost range is not a built-in/c-string type, please provide a valid print element function.)"
);
cout << obj << ' ';
}
) {
if constexpr (ranges::range<Iterable>) {
ranges::for_each(iterable, [&] (const auto& it) { Print(it, separatorDimensions, funcPrintElem); });
cout << separatorDimensions;
} else {
funcPrintElem(iterable);
}
}
The function has a default std::function that can print any built-in type like int, unsigned char, long long etc... and the c-string like char* or const char*, if you have another object like a pair or tuple or an object of your class you can pass a function that prints your object.
You can use the function like this: (you must explicitly tell the function your inner most object like below)
int main() {
cout << "v: " << endl;
vector<uint16_t> v { 1, 2, 3 };
Print<uint16_t>(v);
cout << endl << "ll: " << endl;
list<list<const char*>> ll { { "a", "b" }, { "c", "d" } };
Print<const char*>(ll);
struct smth {
int a;
char b;
};
cout << endl << "smths: " << endl;
vector<smth> smths { { 14, '0' }, { 18, '1' } };
Print<smth>(smths, "\n", [] (const smth& obj) { cout << "a = " << obj.a << ", b = " << obj.b << endl; });
return 0;
}
The function can be found here, maybe I will update in the future to support more things.
Edit: You need to have at least c++20 for this function to work

map strings to class members

Say I have a class / struct like below:
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
And I have a set of strings that associate to each member of A (could be of different integer types, but not non-integer types such as strings), e.g.
"field1" -> A::a
"field2" -> A::b
"field3" -> A::c
Assume that there is always a 1:1 mapping between the strings and members. Is there an elegant way to map each string to each member using something like std::unordered_map?
I want to be able to read and write to each field using the strings as keys, e.g.
A a {1,2,3};
mymap["field1"] = 4; // a.a = 4
mymap["field2"] = 5; // a.b = 5
auto c = mymap["field3"]; // c = a.c = 3
I'm using C++11/14 and can't use boost.
Some more information on the context of this question:
The struct A mentioned in the question are settings of the program. They are hardware configuration parameters and my software program is used to simulate hardware behaviours. These settings/configurations are script-generated structs like above. We read / write these struct members and because the amount of these settings/configurations are so many (a few thousands), it would be convenient to be able to access them by their names as well. This is how & why I want to associate each member with a string. Whether it is subscripting or a function to access the corresponding members does not really matter, but there is such a 1:1 mapping between a string (name of the setting) and the generated struct member. And as I mentioned in the question, these members are of different integer types.
You could make a proxy-class that wraps around a integer, then store this proxy class in a std::unordered_map.
#include <iostream>
#include <functional>
#include <unordered_map>
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
struct ValueWrapper {
using value_type = uint64_t;
template <typename Obj, typename T>
ValueWrapper(Obj& obj, T Obj::*member) {
get = [&, member]() { return obj.*member; };
set = [&, member](value_type value) mutable { obj.*member = value; };
}
ValueWrapper() = default;
ValueWrapper& operator=(value_type value) {
set(value);
return *this;
}
operator value_type() {
return get();
}
std::function<value_type()> get;
std::function<void(value_type)> set;
};
std::unordered_map<std::string, ValueWrapper> make_map(A& a) {
std::unordered_map<std::string, ValueWrapper> map;
map["field1"] = ValueWrapper(a, &A::a);
map["field2"] = ValueWrapper(a, &A::b);
map["field3"] = ValueWrapper(a, &A::c);
return map;
}
int main() {
A a{1,2,3};
auto map = make_map(a);
map["field2"] = 67;
std::cout << a.a << " " << static_cast<int>(a.b) << " " << a.c << std::endl;
std::cout << map["field1"] << " " << map["field2"] << " " << map["field3"] << std::endl;
}
You do get some restrictions depending on the value_type. If you use int64_t you could wrap anything but a uint64_t safely. If you go for a uint64_t you could wrap all the unsigned integers, but not the signed ones safely.
I put the default constructor there to satisfy unordered_maps use of operator[].
template<class V>
struct pseudo_ref_t {
operator V()&& { return getter(); }
void operator=(V v)&&{
setter(std::move(v));
}
std::function<void(V)> setter;
std::function<V()> getter;
};
template<class T, class V>
struct member_t {
friend pseudo_ref_t<V> operator->*( T* t, member_t const& self ) {
return {
[&self, t](V in){ self.setter(*t, std::move(in)); },
[&self, t]()->V{ return self.getter(*t); }
};
}
friend V operator->*( T const* t, member_t const& self ) {
return self.getter(*t);
}
std::function<void(T&, V)> setter;
std::function<V(T const&)> getter;
};
template<class T, class V, class X>
member_t<T, V> make_member( X T::* mem_ptr ) {
return {
[mem_ptr](T& t, V in) {
(t.*mem_ptr) = std::move(in);
},
[mem_ptr](T const& t)->V {
return (t.*mem_ptr);
}
};
}
a member_t<A, uint32_t> can type-erase any member of A that implicitly convertible to/from a uint32_t.
It acts like a smart member pointer.
you said that it could have different integer types, but it is a problem for having an elegant solution. If you can set up on a single type, this would be simple, like the following.
#include <iostream>
#include <map>
struct A {
int a;
int b;
int c;
};
using namespace std;
int main() {
map<string, int A::*> abc = {
{"a", &A::a},
{"b", &A::b},
{"c", &A::c}
};
A aa;
aa.*abc["a"] = 1;
aa.*abc["b"] = 2;
aa.*abc["c"] = 3;
cout << "a = " << aa.a << "(" << aa.*abc["a"] << ")" << endl;
cout << "b = " << aa.b << "(" << aa.*abc["b"] << ")" << endl;
cout << "c = " << aa.c << "(" << aa.*abc["c"] << ")" << endl;
return 0;
}
The main challenge of your question is that different members have different types.
If you make the types the same, you can use a lot of tricks.
For your case, I know you don't want to use Boost or C++17, but just to show you the challenges ahead, let me give you a Boost.Hana, C++17 solution.
#define BOOST_HANA_CONFIG_ENABLE_STRING_UDL
#include <boost/hana/equal.hpp>
#include <boost/hana/string.hpp>
#include <cstdint> // uint
#include <cassert>
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
struct map{
A& aa_;
template<class String>
decltype(auto) operator[](String s) const{
using namespace boost::hana::literals;
if constexpr(s == "field1"_s) return (decltype(aa_.a)&)(aa_.a);
if constexpr(s == "field2"_s) return (decltype(aa_.b)&)(aa_.b);
if constexpr(s == "field3"_s) return (decltype(aa_.c)&)(aa_.c);
}
};
using namespace boost::hana::literals;
void f(uint32_t& a){ a = 3.;}
int main(){
A aa{1,2,3};
map mymap{aa};
mymap["field1"_s] = 4; assert(aa.a == 4);
mymap["field2"_s] = 5; assert(aa.b == 5);
mymap["field3"_s] = 6; assert(aa.c == 6);
auto c = mymap["field3"_s]; assert( c == aa.c );
mymap["blabla"_s]; // is void (not a compile error)
assert( map{aa}["field1"_s] == 4 );
}
From this you can walk backwards and perhaps figure out a C++14, the challenge ahead is that you have to implement your own compile time string literals and equality.
In other words, reimplement your own Hana string strings: https://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/namespaceboost_1_1hana_1_1literals.html
The simple solution is that if you have a script that can generate the struct then you can have your script generate your setters.
Start with a simple function interface.
struct A {
uint32_t a;
uint8_t b;
uint16_t c;
};
typedef std::function<void(A &,string)> SetterType
Then create a lookup table
std::map<std::string,SetterType> Lookup;
and for each field, using your script, generate a parser and setter;
void A_a(A & data, std::string input){
data.a = std::stoi(input);
}
and then
Lookup["a"] = &A_a;
and use it like
Lookup["a"]("10");
If you can't modify the generating script then maybe you can use a third party parser such as swig or clang to read your structures and generate you a parse tree that you can then use to generate your lookup tables.
Or just use a system that already maps strings to C++. A C++ JSON generator.
https://nlohmann.github.io/json/

Cast between two slightly different std::map

I have a std::map defined as follows:
typedef void (SomeClass::*callback_f)();
typedef std::pair<std::string, callback_f> my_pair;
typedef std::map<char,my_pair> my_map1;
There are multiple of these definitions, so there is my_map1, my_map2, etc. and every one of them differs from the other for the class SomeClass, which is SomeClass, SomeClassTwo, etc.
My need is to pass all these maps to a single function, which must have a single signature regardless of the maps, so I defined a generic map:
typedef std::pair<std::string ,void(*)(void)> my_generic_pair;
typedef std::map<char, my_generic_pair> my_generic_map;
And a function which takes it as a parameter:
void myGenericFunction(my_generic_map lmap);
myGenericFunction doesn't really use the callback, but uses the other two values from the my_map{$i}, i=1...n.
The problem is no matter how hard I try to cast between the two maps, the compiler (C++11 compliant GCC) always complains.
Any ideas would be very much appreciated.
If you need to pass all the maps to a single function, then all the maps must be exactly the same type (but you already knew that).
A little re-engineering should give you what you want.
Rather than define your map to hold a specific function pointer signature, simply define one map type that holds a polymorphic callable object - such an object is the std::function<>.
full compilable example (remember to enable c++11):
#include <iostream>
#include <functional>
#include <utility>
#include <string>
#include <map>
// callback_f is any callable that takes no parameters
typedef std::function<void()> callback_f;
typedef std::pair<std::string, callback_f> my_pair;
typedef std::map<char,my_pair> universal_map;
using namespace std;
struct SomeClass {
static void callme_for_all() {
cout << "SomeClass Hears You";
}
};
struct SomeOtherClass {
SomeOtherClass(string name)
: _name { move(name) }
{}
void callme_for_each() const {
cout << "I am called " << _name;
}
private:
string _name;
};
void handle_map(const universal_map& m) {
for (const auto& item : m) {
cout << item.first << ":" << item.second.first << ":";
item.second.second();
cout << endl;
}
}
int main()
{
cout << "Hello World" << endl;
SomeClass a,b,c;
SomeOtherClass x { "x" }, y { "y" }, z { "z" };
universal_map map_someclass {
{ 'A', { "chicken", std::bind(&SomeClass::callme_for_all) } },
{ 'B', { "donkey", std::bind(&SomeClass::callme_for_all) } },
{ 'C', { "turkey", std::bind(&SomeClass::callme_for_all) } },
};
universal_map map_someotherclass {
{ 'A', { "pig", std::bind(&SomeOtherClass::callme_for_each, &x) } },
{ 'B', { "cat", std::bind(&SomeOtherClass::callme_for_each, &y) } },
{ 'C', { "dog", std::bind(&SomeOtherClass::callme_for_each, &z) } },
};
cout << "map for SomeClass - calling static methods" << endl;
handle_map(map_someclass);
cout << endl;
cout << "map for SomeOtherClass - calling instance methods" << endl;
handle_map(map_someotherclass);
return 0;
}
If you have control on your SomeClass, SomeClass2..., you can use inheritance to solve this problem:
class BaseClass
{
public:
virtual ~BaseClass(){}
virtual void f() = 0;
};
class SomeClass : public BaseClass {...};
class SomeClass2 : public BaseClass {...};
typedef void (BaseClass::*callback_f)();
....
Solution 1:
template<typename T>
void myGenericFunction(std::map < char, std::pair < std::string, T > > lmap);
Solution 2 (only if you can't use templates and overloading for whatever reason):
enum MapType {MY_MAP1, MY_MAP2, ...};
void myGenericFunction(void* lmap, MapType typeOfLmap)
{
switch(typeOfLmap)
{
case MY_MAP1:
//do something with ((my_map1*)lmap)
break;
...
}
}
EDIT: edited according to #TonyD's suggestion.

How can I use a vector wrapper class when enclosed in another vector?

Consider a free function from a third part library that expects a std::vector as argument: void foo( std::vector<sometype>& );
Now, I write a wrapper around this type so I can add member functions. To be able to use foo() with that type, I add an access function.
class Wrapper
{
private:
std::vector<sometype> _data;
public:
std::vector<sometype>& data() { return _data; }
const std::vector<sometype>& data() const { return _data; }
//... other stuff
};
This way, I can still use foo():
Wrapper a;
foo( a.data() );
But now consider another function, that expects a vector of vectors of sometype (edit: and that adds elements into that vector) :
void bar( std::vector<std::vector<sometype>>& );
But the datatype I have is std::vector<Wrapper> vec;
Is there any way to use my wrapper type to call bar() ?
What I want to do is this:
std::vector<Wrapper> vec;
bar( ??? );
The point I want to avoid is first call bar() with the required type, and then having to copy one by one the elements into my vector<Wrapper>.
At first, I'd say "No", but maybe there is some smart solution ?
Edit2: to give an example, consider the following toy implementation for bar() with an int root datatype:
void bar( std::vector<std::vector<int>>& vv )
{
std::vector<int> v1 = { 1,2,3 };
std::vector<int> v2 = { 4,5,6 };
vv.push_back(v1);
vv.push_back(v2);
}
[Edited after new comments requiring elements added in the bar function]
A possible solution would be to keep a std::vector<std::vector<sometype>> for the function to use and just operate on a VectorAccessor object referring to the real vectors
#include <iostream>
#include <vector>
struct sometype {
int value;
sometype(int v) : value(v) {}
};
void bar(std::vector<std::vector<sometype>>& par) {
std::cout << "bar() - Before adding new elements:" << std::endl;
for (auto& subvec : par) {
std::cout << "Subvector: {";
for (auto& sometypeItem : subvec) {
std::cout << sometypeItem.value << " ";
}
std::cout << "};" << std::endl;
}
std::vector<sometype> newItem = {32, 33};
par.emplace_back(newItem);
}
class VectorAccessor {
std::vector<std::vector<sometype>>& m_vec;
public:
VectorAccessor(std::vector<std::vector<sometype>>& v) : m_vec(v) {}
template<typename V>
void addVector(V&& vec) {
static_assert(std::is_same<typename std::remove_reference<V>::type,
std::vector<sometype>>::value, "Not the right type");
m_vec.emplace_back(std::forward<V>(vec));
}
std::vector<sometype> accessVector(size_t index) {
return m_vec[index];
}
};
int main(int argc, char ** argv)
{
std::vector<std::vector<sometype>> vec;
VectorAccessor vAcc(vec);
// Add an element through the vector accessor
std::vector<sometype> firstVector = {42};
firstVector.emplace_back(52);
vAcc.addVector(firstVector);
// Call bar and add a few elements
bar(vec);
// Now access stuff with the usual wrapper
std::cout << "Elements added by bar:" << std::endl;
std::cout << "Subvector: {";
for (auto& sometypeItem : vAcc.accessVector(1)) {
std::cout << sometypeItem.value << " ";
}
std::cout << "};" << std::endl;
return 0;
}
Example
Out of the box, calling a function taking a vector<vector<something> won't work with a vector<Wrapper>, because their type is different, and the compiler explicitely expects the former.
I don't think there is any way this form of type substitution could work in C++.
Workaround
There's a workaround to everyhting : you could use conversions in your own code to let the magic happen.
Let me explain.
If the function you intend to use takes a vector<vector<something>>, in C++, you basically have to give it a vector<vector<something>>. So you can't create your vector as a vector<Wrapper> and avoid converting it to a vector<vector<something>>.
On the other hand, you can
use a vector<vector<something> in which you will push instances of Wrapper (using an implicit conversion).
if you need Wrapper functionnality, you can convert your vector<something> using a conversion constructor.
Let's take that example :
#include <iostream>
#include <vector>
using namespace std;
//Templated class wrapper. It does not have to be templated though.
template<typename T>
class Wrapper{
private:
//Here is our inner vector.
vector<T> vect;
public:
//here is our implicit convertion operator :
operator vector<T>& () const {return this->vect;}
//A function so that we can push some stuff in it
void push(T elem){
this->vect.push_back(elem);
}
//here is some additional functionnality in top of vector;
void print(){
int i = 0;
for(i=0;i<this->vect.size();i++){
cout << vect[i] << " ";
}
cout << endl;
}
//this is our very simple conversion constructor
Wrapper<T>(vector<T> vect){
this->vect = vect;
}
//we still need a normal constructor
Wrapper<T>(){}
};
//A function that takes a vector of vectors.
vector<int> concat(vector<vector<int>> vectors){
int i = 0,j=0;
vector<int> result;
for(i=0;i<vectors.size();i++){
for(j=0;j<vectors[i].size();j++){
result.push_back(vectors[i][j]);
}
}
return result;
}
int main()
{
//Let's create an instance of Wrapper and fill it.
Wrapper<int>ex;
ex.push(1);
ex.push(2);
//And yet another one
Wrapper<int>ex2;
ex2.push(5);
ex2.push(6);
//Here we create precisely what the 'concat' function wants:
//namely a vector<vector<int>>.
vector<vector<int>> vectors;
//you can push Wrappers in it, since the conversion will take place.
vectors.push_back(ex);
vectors.push_back(ex2);
//this function call will be successful, since the type of
//vectors is vector<vector<int>>
vector<int> res = concat(vectors);
//Now if you want to use the wrapper functionnality on any
//vector<int>, just convert it on-demand.
//The constructor is extra light-weight in terms of computing power
//as you can see above.
Wrapper<int>(res).print();
Wrapper<int>(vectors[0]).print();
}
P.S. The push_back function will copy the element, so if your function does modify your vector, it won't be reflected on the Wrapper, since it's a copy of its inner vector that has been modified. Using a real vector<something> and push_back would result in the same behaviour.
instead of std::vector<Wrapper> vec;
use
std::vector< std::vector<sometype> > vec;
anyway, you can insert your Wrapper objects into vec
vec.push_back(a.data());
and then call bar(vec);
Ok, so I came up with something that seems to work, although there could be some issues left. The idea is to wrap the vector of vectors into some global wrapper, and then the initial wrapper accessing the data inside it using pointers.
Say with the following toy bar() function:
void bar(std::vector<std::vector<int>>& par)
{
std::vector<int> v1 = { 1,2,3 };
par.push_back(v1);
}
The two wrappers:
struct GlobalWrapper
{
std::vector<std::vector<int>> _data;
size_t size() const { return _data.size(); }
std::vector<int>& Get( size_t i ) { return _data[i]; }
const std::vector<int>& Get( size_t i ) const { return _data[i]; }
};
struct Wrapper
{
std::vector<int>* _data;
void DoSomething() const
{
cout << "values: ";
std::copy( _data->begin(), _data->end(), std::ostream_iterator<int>(std::cout, " "));
}
Wrapper( std::vector<int>& value ) : _data(&value)
{
}
};
And a test program:
int main(int argc, char ** argv)
{
GlobalWrapper gw;
cout << "size before=" << gw.size() << endl;
bar( gw._data );
cout << "size after=" << gw.size() << endl;
Wrapper w = gw.Get(0); // get first element and do something with it
w.DoSomething();
return 0;
}
One issue left: ownership of data. Probably needs some smart pointers.
Running code is here.