Trying to learn boost::intrusive Q2 - c++

if I uncomment these
//BaseList baselist;
//MemberList memberlist;
outside the loop and comment out the ones inside the loop it crashes. I need to be able to have the baselist (and memberlist) outside any loop. How is this achieved?
Edit
The actual problem I am trying to solve in it's simplest form is this.
I want to have a std::vector of MyClass, call it AllThingsBunchedTogether.
I also want to have a std::vector of BaseList, call it AllThingsSpreadOut.
So
AllThingsBunchedTogether might contain (just the anInt1 part for the sake of compactness): 1,2,1,10,2,3,4,4,5,9,10,10.
AllThingsSpreadOut might contain (zero not used for now) at [1] 1,1 at [2] 2,2 at [3] 3 at [4] 4,4 at [5] 5 at [9] 9 at [10] 10,10,10.
Note that the numbers themselves aren't be stored in the BaseList, but e.g., the MyClass(1, "John").
At [1] it could be "Mike", "John", at [2] it could be "Mike", "Dagobart" at [3]
"John" ... at [10] "John" "Mike" "Dagobart" etc so that there no duplicates in
any of the BaseList at AllThingsSpreadOut[i] since each MyClass in each
BaseList hashes to a different value (anInt1 + Name).
In essence, anInt1 tells where the MyClass lives in AllThingsSpreadOut, but anInt1 + name guarantees uniqueness within each BaseList.
So the idea is that AllThingsSpreadOut is a vector of BaseList where at each BaseList at vector location is a list of similar things.
Then, when I remove things from AllThingsBunchedTogether (not by a clear, but by a search to remove some items like in the code below IsMarkedToDelete), they will automatically disappear from the corresponding AllThingsSpreadOut.
AllThingsSpreadOut acts as a sort for AllThingsBunchedTogether, with intrusive semantics. AllThingsBunchedTogether allows superfast access through [].
End Edit
#include <vector>
#include <iostream>
#include <boost/intrusive/list.hpp>
using namespace boost::intrusive;
class MyClass : public list_base_hook<link_mode<auto_unlink>> // This is a derivation hook
{
public:
std::string name;
bool bIsMarkedToDelete;
int anInt1;
public:
list_member_hook<link_mode<auto_unlink>> member_hook_; // This is a member hook
MyClass(std::string n, int i) : name(n), anInt1(i), bIsMarkedToDelete(false) {}
};
bool IsMarkedToDelete(const MyClass &o)
{
return o.bIsMarkedToDelete;
}
//Define a list that will store MyClass using the public base hook
typedef list<MyClass, constant_time_size<false>> BaseList;
// Define a list that will store MyClass using the public member hook
typedef list<MyClass,
member_hook<MyClass, list_member_hook<link_mode<auto_unlink>>, &MyClass::member_hook_>,
constant_time_size<false> > MemberList;
int main()
{
bool done = false;
std::vector<MyClass> values;
std::string names[] = {"John", "Mike", "Dagobart"};
//BaseList baselist;
//MemberList memberlist;
int i = 0;
while(!done)
{
// Create several MyClass objects, each one with a different value
for (int j = 0; j < 11; ++j)
values.emplace_back(names[j % 3], j);
BaseList baselist;
MemberList memberlist;
// Now insert them in t-he reverse order in the base hook list
for (auto& e : values)
{
baselist.push_front(e);
memberlist.push_back(e);
}
// Now test lists
auto rbit(baselist.rbegin());
auto mit(memberlist.begin());
auto it(values.begin()), itend(values.end());
// Test the objects inserted in the base hook list
for (; it != itend; ++it, ++rbit)
{
if (&*rbit != &*it)
return 1;
}
// Test the objects inserted in the member hook list
for (it = values.begin(); it != itend; ++it, ++mit)
{
if (&*mit != &*it)
return 1;
}
# if 0
for(auto& e : values)
std::cout << e.anInt1 << "\n";
for(auto& e : baselist)
std::cout << e.anInt1 << "\n";
for(auto& e : memberlist)
std::cout << e.anInt1 << "\n";
#endif // 0
if(2 == i)
{
for(auto& e: values)
std::cout << e.name << "\n";
for(auto& e: values)
{
if("Mike" == e.name)
e.bIsMarkedToDelete = true;
}
values.erase(
std::remove_if(values.begin(), values.end(), IsMarkedToDelete), values.end());
}
if(i++ > 3)
{
values.clear();
done = true;
}
std::cout << "\n";
std::cout << values.size() << "\n";
std::cout << baselist.size() << "\n";
std::cout << memberlist.size() << "\n";
}
}

I've seen it late, but anyways, here goes:
What you describe matches exactly the implementation of an intrusive hash table of MyClass elements, where
anInt1 is the hash (the bucket identifier) for an element
the bucket lists are implemented as linked lists
equality is defined as equality of (anInt1, Name)
So really, your program could just be:
Live On Coliru
std::unordered_set<MyClass> values {
{ "John", 0 }, { "Mike", 1 }, { "Dagobart", 2 },
{ "John", 3 }, { "Mike", 4 }, { "Dagobart", 5 },
{ "John", 6 }, { "Mike", 7 }, { "Dagobart", 8 },
{ "John", 9 }, { "Mike", 10 },
};
for(int i = 0; i<=3; ++i) {
if(2 == i) {
for(auto& e: values) std::cout << e.name << " "; std::cout << "\n";
for(auto& e: values) e.bIsMarkedToDelete |= ("Mike" == e.name);
for(auto it=begin(values); it!=end(values);) {
if (it->bIsMarkedToDelete) it = values.erase(it);
else ++it;
}
}
std::cout << "i=" << i << ", values.size(): " << values.size() << "\n";
}
values.clear();
std::cout << "Done\n";
if you really wanted contiguous storage, I can only assume you wanted this for performance
you do not want to use pointers instead of objects, since that simply negates the memory layout ("AllThingsBunchedTogether") benefits and you'd be better of with the unordered_set or unodered_map as above
you do not want to use auto_unlink mode, since it cripples performance (by doing uncontrolled deletion triggers, by inhibiting constant-time size() and by creating thread safety issues)
instead, you should employ the above stratagy, but with boost::intrusive::unordered_set instead see http://www.boost.org/doc/libs/1_57_0/doc/html/intrusive/unordered_set_unordered_multiset.html
Here, again, is a proof-of-concept:
Live On Coliru
#include <vector>
#include <iostream>
#include <boost/intrusive/unordered_set.hpp>
#include <vector>
//#include <functional>
//#include <algorithm>
namespace bic = boost::intrusive;
struct MyClass : bic::unordered_set_base_hook<bic::link_mode<bic::auto_unlink>>
{
std::string name;
int anInt1;
mutable bool bIsMarkedToDelete;
MyClass(std::string name, int i) : name(name), anInt1(i), bIsMarkedToDelete(false) {}
bool operator==(MyClass const& o) const { return anInt1 == o.anInt1 && name == o.name; }
struct hasher { size_t operator()(MyClass const& o) const { return o.anInt1; } };
};
typedef bic::unordered_set<MyClass, bic::hash<MyClass::hasher>, bic::constant_time_size<false> > HashTable;
int main() {
std::vector<MyClass> values {
MyClass { "John", 0 }, MyClass { "Mike", 1 }, MyClass { "Dagobart", 2 },
MyClass { "John", 3 }, MyClass { "Mike", 4 }, MyClass { "Dagobart", 5 },
MyClass { "John", 6 }, MyClass { "Mike", 7 }, MyClass { "Dagobart", 8 },
MyClass { "John", 9 }, MyClass { "Mike", 10 },
};
HashTable::bucket_type buckets[100];
HashTable hashtable(values.begin(), values.end(), HashTable::bucket_traits(buckets, 100));
for(int i = 0; i<=3; ++i) {
if(2 == i) {
for(auto& e: values) std::cout << e.name << " "; std::cout << "\n";
for(auto& e: values) e.bIsMarkedToDelete |= ("Mike" == e.name);
values.erase(std::remove_if(begin(values), end(values), std::mem_fn(&MyClass::bIsMarkedToDelete)));
}
std::cout << "i=" << i << ", values.size(): " << values.size() << "\n";
std::cout << "i=" << i << ", hashtable.size(): " << hashtable.size() << "\n";
}
values.clear();
std::cout << "Done\n";
}

Here's the error message, which you omitted:
Assertion `node_algorithms::inited(to_insert)' failed.
From this we can understand that an element is being inserted twice. This isn't valid with intrusive containers in general.
When you have your lists inside the loop, they are destroyed and recreated each time. But when they are outside, you never clear them, and you also never clear values, so this sequence occurs:
Add 11 elements to values.
Add all values to the lists.
Add 11 elements to values; it still has the previous 11 so now 22 elements.
Add all values to the lists. Crash on the first one, because it is already in a list.
One solution is to add values.clear() at the top of the while(!done) loop.

Related

C++ - Merging two sorted vectors of different, unique types with a common attribute into a new sorted vector

There are two types, A and B. These types both have a common attribute, a key.
There are two vectors of type A and B. These vectors are sorted by key ascending. The vectors are unique in regards to their keys - ie if a key is present in A_vect it is guaranteed to not be present in B_vect and vice versa.
The goal is to take the keys in vector A_vect, construct new B types and put them into B_vect, such that B contains it's original keys and it's new keys. The new B_vect should also be sorted.
I have this code snippet so far:
#include <iostream>
#include <vector>
#include <algorithm>
struct A
{
A(int i)
{
key = i;
}
int key;
};
struct B
{
B(int i)
{
key = i;
}
B()
{
}
int key;
};
int main()
{
std::vector<A> a_diff_sorted_vect = {1, 3};
std::vector<B> b_sorted_vect = {2, 4};
std::transform
(
a_diff_sorted_vect.begin(), a_diff_sorted_vect.end(),
std::back_inserter(b_sorted_vect),
[](const A &a) -> B
{
B b;
b.key = a.key;
return b;
}
);
std::cout << "Printing b_sorted_vect" << "\n";
for(auto element : b_sorted_vect)
{
std::cout << std::to_string(element.key) << "\n";
}
std::cout << "Finished printing b_sorted_vect" << "\n";
/*
std::sort
(
b_sorted_vect.begin(),
b_sorted_vect.end(),
[](B lhs, B rhs)
{
return lhs.key < rhs.key;
}
);
std::cout << "Printing b_sorted_vect" << "\n";
for(auto element : b_sorted_vect)
{
std::cout << std::to_string(element.key) << "\n";
}
std::cout << "Finished printing b_sorted_vect" << "\n";
*/
return 0;
}
Output:
2
4
1
3
Desired output:
1
2
3
4
Ideally I would like to avoid the top-down std::sort at the end, as I think there might be a more efficient way to do that during construction and merging. The std::sort is my current solution.
The use of a third temporary vector might be necessary, which is fine.
This should be done with C++11, using boost if necessary.

Convert multi-line if statment to single line with map

Note: c++98
I am a little new to C++ and I want to clean my code up. I have an if statment that checks the data type in an array, and if it matches then it is to execute the corresponding statement.
I want to convert this multi-line if statment to a single line that checks if any of these types exist in the map, and if they do execute it.
My code:
if (boost::iequals(sqlBufferTypes[i][j], "INTEGER") ||
boost::iequals(sqlBufferTypes[i][j], "INT") ||
boost::iequals(sqlBufferTypes[i][j], "BIGINT") ||
boost::iequals(sqlBufferTypes[i][j], "uint8_t") ||
boost::iequals(sqlBufferTypes[i][j], "uint16_t") ||
boost::iequals(sqlBufferTypes[i][j], "LONG"))
{
// do stuff
}
and would like to convert it similar to something like:
map<int, string> dataTypes;
dataTypes[1,"INT"];
dataTypes[2,"BIGINT"];
dataTypes[3,"uint8_t"];
dataTypes[4,"uint16_t"];
dataTypes[5,"LONG"];
if (boost::iequals(dataTypes.begin(), dataTypes.end())
{
// do stuff
}
I suppose the real challenge is to have a map<> that compares the keys case-insensitively.
You do that by using a comparison predicate:
struct ci_less {
bool operator()(std::string_view a, std::string_view b) const {
return boost::lexicographical_compare(a, b, boost::is_iless{});
}
};
You declare the map to use that predicate:
std::map<std::string, int, ci_less> const dataTypes {
{ "INT", 1 },
{ "BIGINT", 2 },
{ "uint8_t", 3 },
{ "uint16_t", 4 },
{ "LONG", 5 },
};
Note that it is now const, and I flipped the key/value pairs. See below
Some tests: Live On Coliru
// your sqlBufferTypes[i][j] e.g.:
for (std::string const key : { "uint32_t", "long", "lONg" }) {
if (auto match = dataTypes.find(key); match != dataTypes.end()) {
std::cout << std::quoted(key) << " maps to " << match->second;
// more readable repeats lookup:
std::cout << " or the same: " << dataTypes.at(key) << "\n"; // throws unless found
} else {
std::cout << std::quoted(key) << " not found\n";
}
}
Prints
"uint32_t" not found
"long" maps to 5 or the same: 5
"lONg" maps to 5 or the same: 5
Flipping Key/Value
Dictionaries have a key field for lookup in all languages/libraries. So, to lookup in reverse you end up doing a linear search (just look at each element).
In Boost you can have your cake and eat it by defining a Multi Index Container.
Multi Index
This can facilitate lookup by multiple indices, including composite keys. (Search my answers for more real-life examples)
Live On Coliru
#include <boost/algorithm/string.hpp> // for is_iless et al.
#include <string_view>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <iostream> // for std::cout
#include <iomanip> // for std::quoted
#include <boost/locale.hpp>
namespace bmi = boost::multi_index;
struct ci_less {
bool operator()(std::string_view a, std::string_view b) const {
return boost::lexicographical_compare(a, b, boost::is_iless{});
}
};
struct DbType {
std::string_view name;
int type_id;
friend std::ostream& operator<<(std::ostream& os, DbType const& t) {
return os << "DbType{" << std::quoted(t.name) << ", " << t.type_id << "}";
}
};
using Map = bmi::multi_index_container<
DbType,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_id>,
bmi::member<DbType, int, &DbType::type_id> >,
bmi::ordered_unique<
bmi::tag<struct by_name>,
bmi::member<DbType, std::string_view, &DbType::name>, ci_less>
>
>;
int main() {
Map dataTypes {
{ "INT", 1 },
{ "BIGINT", 2 },
{ "uint8_t", 3 },
{ "uint16_t", 4 },
{ "LONG", 5 },
};
auto& idx = dataTypes.get<by_name>();
// your sqlBufferTypes[i][j] e.g.:
for (std::string_view const key : { "uint32_t", "long", "lONg" }) {
if (auto match = idx.find(key); match != idx.end()) {
std::cout << std::quoted(key) << " -> " << *match << std::endl;
} else {
std::cout << std::quoted(key) << " not found\n";
}
}
}
Prints
"uint32_t" not found
"long" -> DbType{"LONG", 5}
"lONg" -> DbType{"LONG", 5}
Bimaps
Boost Bimap is a specialization of that for maps. It has fewer options, and notably adds operator[] style interface back.
using Map = boost::bimap<
int,
boost::bimaps::set_of<std::string_view, ci_less>>;
Sadly the constructor doesn't support initializaer lists, but we can use the iterator interface, and then we use the right view of the bimap to do lookups by name:
Live On Coliru
static const Map::relation s_mappings[] = {
{ 1, "INT" },
{ 2, "BIGINT" },
{ 3, "uint8_t" },
{ 4, "uint16_t" },
{ 5, "LONG" },
};
Map const dataTypes { std::begin(s_mappings), std::end(s_mappings) };
// your sqlBufferTypes[i][j] e.g.:
auto& vw = dataTypes.right;
for (std::string_view const key : { "uint32_t", "long", "lONg" }) {
if (auto match = vw.find(key); match != vw.end()) {
std::cout << std::quoted(key) << " -> " << match->second << "\n";
} else {
std::cout << std::quoted(key) << " not found\n";
}
}
Prints
"uint32_t" not found
"long" -> 5
"lONg" -> 5
I think you switched around the key and id in your example, judging by your description.
However, you can do what you want fairly easily by introducing a custom compare for the map. An easy way is to create a lambda function first:
auto compare = [](const std::string& a, const std::string& b) {return boost::iequals(a, b); };
std::map < std::string, int, decltype(compare)> mymap = { {"INT", 1},{"BIGINT", 2 }};//and so on
Then you can do:
if (mymap.count(sqlBufferTypes[i][j])) {//in c++20 can use "contains" instead of "count"
//do stuff
}
If all you want do is check and dont need the ID later, then you should use a std::set instead of map.
c++98 NOTE
In c++98 you cannot use a lambda, however you can still make the compare predicate with a regular struct:
struct compare : public std::binary_function<string, string, bool>
{
bool operator()(const string& a, const string& b) const
{
return boost::iequals(a,b);
}
};

Better way to map string fields to variables

I have some global variables that will be assigned a value once the configuration file is read.
bool bar1;
int bar2;
string bar3;
I read the configuration file which looks like below:
foo1 = 12
foo2 = 0
foo3 = 1
...
void func()
{
//read file into a std::map mp
for(auto i:mp)
{
if(i.first=="foo1")
bar1 = i.second;
else if(i.first=="foo2")
bar2 = i.second;
else if(i.first=="foo3")
bar3 = i.second;
.....
}
}
I have a lot of such variables to initialize from a file. Is there a better way to do this because this will bloat my function.
PS:I am still stuck with C++03.
In my comment, I elaborated a bit on the idea of Jabberwocky to use a std::map.
Actually, we do similar things in our S/W for configuration and similar things. The only difference – we don't use a std::map for this but a pre-defined array. (I didn't like the idea that something has to be done at run-time which actually never changes after compiling.) To demonstrate the concept I made a little MCVE:
#include <iostream>
#include <cassert>
#include <cstring>
#include <algorithm>
#include <map>
int main()
{
// variables
int bar1 = 0, bar2 = 0, bar3 = 0;
// symbol table
const struct Entry { const char *key; int *pVar; } table[] = {
{ "foo1", &bar1 },
{ "foo2", &bar2 },
{ "foo3", &bar3 }
};
const size_t nTable = sizeof table / sizeof *table;
// check that table has correct order
assert([&]()
{
for (size_t i = 1; i < nTable; ++i) {
if (strcmp(table[i - 1].key, table[i].key) >= 0) return false;
}
return true;
}());
// use table in tests
std::pair<const char*, int> mp[] = {
{ "foo1", 123 },
{ "foo2", 234 },
{ "foo3", 345 },
{ "wrong", 666 }
};
// evaluate mp of OP
for (auto i : mp) {
const Entry e = { i.first, 0 };
const auto iter
= std::lower_bound(std::begin(table), std::end(table), e,
[](const Entry &e1, const Entry &e2) { return strcmp(e1.key, e2.key) < 0; });
if (iter != std::end(table) && strcmp(iter->key, i.first) == 0) *iter->pVar = i.second;
else std::cerr << "Unknown var '" << i.first << "'!\n";
}
// print result
std::cout
<< "bar1: " << bar1 << '\n'
<< "bar2: " << bar2 << '\n'
<< "bar3: " << bar3 << '\n';
// done
return 0;
}
Output:
Unknown var 'wrong'!
bar1: 123
bar2: 234
bar3: 345
Live Demo on coliru
The essential part is the struct Entry which groups the name of an option with the address of the corresponding variable. This could be used to store pairs of names and variable addresses in a std::map.
I used instead a pre-sorted array. (Sorting the keys manually in programming is not that difficult – in case of accidents the assert() will alert.)
In our productive S/W, we didn't use addresses of variables but method pointers to setter functions as the destination variables have varying types and the values (provide as string) are subject of a resp. parsing. However, these method pointers are compile-time solvable → the whole table can be static. Hence, the effort for building up the table for each function call is prevented. In this demo, the table stores addresses to local variables. This let me feel that a static table could be a bad idea (and I even didn't try it).
Upon request, here another demonstration using method pointers to setter methods:
#include <iostream>
#include <cassert>
#include <cstring>
#include <string>
#include <algorithm>
class Object {
private:
// some member variables:
int var1, var2;
std::string var3;
double var4;
public:
Object(): var1(), var2(), var4() { }
friend std::ostream& operator<<(std::ostream &out, const Object &obj);
// the setter methods
void setVar1(const char *value) { var1 = atoi(value); }
void setVar2(const char *value) { var2 = atoi(value); }
void setVar3(const char *value) { var3 = value; }
void setVar4(const char *value) { var4 = strtod(value, nullptr); }
// the config method to set value by text
void config(const char *key, const char *value)
{
// symbol table
static const struct Entry {
const char *key; // the symbol
void (Object::*set)(const char*); // the corresponding setter method
} table[] = {
{ "var1", &Object::setVar1 },
{ "var2", &Object::setVar2 },
{ "var3", &Object::setVar3 },
{ "var4", &Object::setVar4 }
};
enum { nTable = sizeof table / sizeof *table };
// check that table has correct order (paranoid - debug only code)
assert([&]()
{
for (size_t i = 1; i < nTable; ++i) {
if (strcmp(table[i - 1].key, table[i].key) >= 0) return false;
}
return true;
}());
// find setter by key
const Entry e = { key, nullptr };
const auto iter
= std::lower_bound(std::begin(table), std::end(table), e,
[](const Entry &e1, const Entry &e2) { return strcmp(e1.key, e2.key) < 0; });
if (iter != std::end(table) && strcmp(iter->key, key) == 0) {
(this->*iter->set)(value);
} else std::cerr << "Unknown var '" << key << "'!\n";
}
};
std::ostream& operator<<(std::ostream &out, const Object &obj)
{
return out
<< "var1: " << obj.var1 << ", var2: " << obj.var2
<< ", var3: '" << obj.var3 << "', var4: " << obj.var4;
}
int main()
{
Object obj;
// print obj before config:
std::cout << "obj: " << obj << '\n';
// configure obj
std::pair<const char*, const char*> config[] = {
{ "var1", "123" },
{ "var2", "456" },
{ "var3", "text" },
{ "var4", "1.23" },
{ "evil", "666" }
};
for (const auto& entry : config) {
obj.config(entry.first, entry.second);
}
// print obj after config:
std::cout << "obj: " << obj << '\n';
// done
return 0;
}
Output:
obj: var1: 0, var2: 0, var3: '', var4: 0
Unknown var 'evil'!
obj: var1: 123, var2: 456, var3: 'text', var4: 1.23
The contents of table (in Object::config()) is static const and will be built at compile-time (and hopefully "burnt" into the binary). Hence, the multiple calls of Object::config() have the only effort of binary search of the matching key and calling the setter in case of success.
A essential pre-condition is that all setter methods have the same signature. Otherwise, storing them in an array wouldn't be possible as they all have to be compatible to the method pointer element in the array.
Live Demo on coliru

How to determine size from (nested) std::initializer_list?

New to C++ and trying to wrap my head around initializer_list.
I'm making a Matrix class that effectively stores a 2d array of double values. I don't get the project on a structural level. Like okay we make a Matrix class that essentially stores a 2D array of data. But it needs to be able to store any size array, so it must use a dynamically allocated array. But std::array isn't allowed.
I have no idea how to access the items in the i_list. If they're passed in like
Matrix a = {{1, 2}, {3, 4}};
then according to the documentation I've seen, my only options for interaction with that information in the constructor are list.begin() which either points to the {1, 2} and list.end() which points to the {3,4}
std::vector and std::array are prohibited by the project description, and non-dynamic arrays obviously can't take in variables for size.
So how do I make this able to read a matrix of any size, and how do I take those values from my i_list and store them into something nondynamic?
I'm envisioning something like
Matrix::Matrix(const initializer_list & list) {
double * mat[/*somehow find out size without dynamic allocation*/];
for (double* i : mat) {
*i = list[i]; //not how i_list works apparently
}
}
Project description says:
You MAY NOT use library classes such as std::array, std::vector, std::list, etc. for this project. You must implement your Matrix class internally using a dynamically allocated array
initializer_lists are very cheap containers of [references to] temporary objects.
You can iterate over them as if they were arrays. In addition they also have a size() member so you can query their size.
Here is an example of passing a '2d' initializer_list to a function (which could easily be an constructor):
#include <initializer_list>
#include <iostream>
using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;
void info(list_of_list_of_doubles lld)
{
std::cout << "{\n";
for (auto& ld : lld) {
std::cout << " {";
auto sep = " ";
for (auto& d : ld) {
std::cout << sep << d;
sep = ", ";
}
std::cout << " }\n";
}
std::cout << "}\n";
}
int main()
{
info({
{ 1,2,3 },
{ 4.0, 5.0, 6.0 }
});
}
expected output:
{
{ 1, 2, 3 }
{ 4, 5, 6 }
}
Printing out the contents of the list is pretty simple, but what if I want to save them non-dynamically? I'm making a class constructor, and I want to have access to that data.
OK, so the requirement is that the storage in the class is non-dynamic (i.e. a fixed size).
I am going to make some assumptions:
let's say that the target class is a 3x3 matrix
any non-specified items in the initializer_list should be assumed to be zero.
passing in more than 3 rows or columns is a logic error and should cause an exception to be raised
Here's one (of many) ways:
#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <algorithm>
using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;
struct matrix
{
matrix(list_of_list_of_doubles lld)
: _storage {}
{
if (lld.size() > 3)
throw std::invalid_argument("too many rows");
auto row_idx = std::size_t { 0 };
for (auto& row : lld) {
if (row.size() > 3)
throw std::invalid_argument("too many columns");
std::copy(std::begin(row), std::end(row), std::begin(_storage[row_idx]));
++row_idx;
}
}
double _storage[3][3];
};
std::ostream& operator<<(std::ostream& os, const matrix& m)
{
std::cout << "{\n";
for (auto& ld : m._storage) {
std::cout << " {";
auto sep = " ";
for (auto& d : ld) {
std::cout << sep << d;
sep = ", ";
}
std::cout << " }\n";
}
return std::cout << "}";
}
int main()
{
matrix m({
{ 1,2,3 },
{ 4.1, 5.2, 6.3 },
{ 2.01, 4.5 } // ,0
});
std::cout << m << std::endl;
}
but I wanted a dynamically-sized 2-d array...
Oh go on then...
#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>
using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;
std::size_t total_extent(const list_of_list_of_doubles& lld)
{
return std::accumulate(std::begin(lld), std::end(lld), std::size_t(0),
[](auto tot, auto& container) {
return tot + container.size();
});
}
struct matrix
{
using value_storage = std::unique_ptr<double[]>;
using index_storage = std::unique_ptr<std::size_t>;
matrix(list_of_list_of_doubles lld)
: _total_extent { total_extent(lld) }
, _rows { lld.size() }
, _indecies { new std::size_t[_rows] }
, _storage { new double [_total_extent] }
{
auto istorage = _storage.get();
auto iindex = _indecies.get();
for (auto& row : lld) {
*iindex++ = istorage - _storage.get();
istorage = std::copy(std::begin(row), std::end(row), istorage);
}
}
std::size_t rows() const {
return _rows;
}
const double* column(std::size_t row) const {
return std::addressof(_storage[_indecies[row]]);
}
std::size_t column_size(std::size_t row) const {
return row == _rows - 1
? _total_extent - _indecies[row]
: _indecies[row + 1] - _indecies[row];
}
std::size_t _total_extent, _rows;
std::unique_ptr<std::size_t[]> _indecies;
std::unique_ptr<double[]> _storage;
};
std::ostream& operator<<(std::ostream& os, const matrix& m)
{
std::cout << "{\n";
for (std::size_t row = 0 ; row < m.rows() ; ++row) {
std::cout << " {";
auto sep = " ";
for (std::size_t col = 0 ; col < m.column_size(row) ; ++col) {
std::cout << sep << m.column(row)[col];
sep = ", ";
}
std::cout << " }\n";
}
return std::cout << "}";
}
int main()
{
matrix m({
{ 1,2,3 },
{ 4.1, 5.2, 6.3 },
{ 2.01, 4.5 } // ,0
});
std::cout << m << std::endl;
}
Perhaps, you are looking for something like this:
struct Matrix {
Matrix(std::initializer_list<std::initializer_list<double>> m) {
int max=0;
for (auto l: m)
if (m.size()>max)
max= m.size();
std::cout << "your matriz seems to be: "
<< m.size() << ' ' << max << std::endl;
}
};

How to clone a hook with Boost Intrusive?

I'm learning Boost Intrusive library. I have a problem when I try to copy a STL container. I use a std::vector. It contains elements of class list_base_hook in the mode auto_unlink but the information about the node (is_linked()) is lost when you call the copy constructor.
I have the following code:
class helper_class
{
public:
helper_class(void) { /* ... */ }
helper_class(const helper_class& hc) { /* ... */ }
helper_class(helper_class&& hc) { /* ... */ }
helper_class & operator=(const helper_class& hc) { /* ... */ }
helper_class & operator=(helper_class&& hc) { /* ... */ }
virtual ~helper_class(void) { /* ... */ }
// ...
};
typedef list_base_hook<link_mode<auto_unlink> > auto_hook;
class my_class : public auto_hook
{
public:
friend bool operator==(const my_class &a, const my_class &b)
{
return (a.int_ == b.int_) &&
(a.helper_class_ == b.helper_class_);
}
int int_;
helper_class* helper_class_;
// ...
};
typedef list<my_class, constant_time_size<false> > my_class_list;
struct new_cloner
{
my_class *operator()(const my_class &clone_this)
{ return new my_class(clone_this); }
};
struct delete_disposer
{
void operator()(my_class *delete_this)
{ delete delete_this; }
};
int main()
{
// ...
helper_class the_helper_class;
const int MaxElem = 100;
std::vector<my_class> nodes(MaxElem);
std::vector<my_class> copy_nodes(MaxElem);
my_class_list list;
for(int i = 0; i < MaxElem; ++i) {
nodes[i].int_ = i;
nodes[i].helper_class_ = &the_helper_class;
}
list.insert(list.end(), nodes.begin(), nodes.end());
my_class_list cloned_list;
cloned_list.clone_from(list, new_cloner(), delete_disposer());
copy_nodes = nodes;
std::cout << "nodes[0].is_linked() : "
<< ((nodes[0].is_linked()) ? "LINKED":"NO-LINKED")
<< std::endl;
std::cout << "copy_nodes[0].is_linked() : "
<< ((copy_nodes[0].is_linked()) ? "LINKED":"NO-LINKED")
<< std::endl;
std::cout << "list[0].is_linked() : "
<< (((*list.begin()).is_linked()) ? "LINKED":"NO-LINKED")
<< std::endl;
std::cout << "cloned_list[0].is_linked() : "
<< (((*cloned_list.begin()).is_linked()) ? "LINKED":"NO-LINKED")
<< std::endl;
cloned_list.clear_and_dispose(delete_disposer());
// ...
return 0;
};
Standard output:
nodes[0].is_linked() : LINKED
copy_nodes[0].is_linked() : NO-LINKED
list[0].is_linked() : LINKED
cloned_list[0].is_linked() : LINKED
Why the vector copy_nodes isn't linked?
Thanks you.
Why would you expect a copied node to be in a collection?
If you print a book twice, do you expect it to be magically end up in the same library as the other book that was printed months ago?
It's just a different object. Also known as a copy.
If your copy would "magically" clone the hook as well, that would either break container invariants, or raise the question /where/ the copy should be inserted in the container.
After some serious debating, I figured you might want to know how to clone the list along with the values in a vector:
my_class_list cloned_list;
std::vector<my_class> cloned_nodes;
cloned_nodes.reserve(MaxElem);
cloned_list.clone_from(
list,
[&cloned_nodes](my_class const&v) { cloned_nodes.push_back(v); return &cloned_nodes.back(); },
[](my_class*){}
);
There's no delete here (because you can just destroy the vector anyway). Here's a full demo of this
Live On Coliru
#include <boost/intrusive/list.hpp>
using namespace boost::intrusive;
struct my_class : list_base_hook<link_mode<auto_unlink> > { };
typedef list<my_class, constant_time_size<false> > my_class_list;
#include <iostream>
int main()
{
const int MaxElem = 100;
std::vector<my_class> nodes(MaxElem);
//////////////////////////////////////////////
// He's making a list
my_class_list list;
list.insert(list.end(), nodes.begin(), nodes.end());
//////////////////////////////////////////////
// He's checking it twice
my_class_list cloned_list;
std::vector<my_class> cloned_nodes;
cloned_nodes.reserve(MaxElem);
cloned_list.clone_from(
list,
[&cloned_nodes](my_class const&v) { cloned_nodes.push_back(v); return &cloned_nodes.back(); },
[](my_class*){}
);
std::cout << std::boolalpha;
std::cout << "nodes[0].is_linked() : " << nodes[0].is_linked() << std::endl;
std::cout << "cloned_nodes[0].is_linked(): " << cloned_nodes[0].is_linked() << std::endl;
std::cout << "list[0].is_linked() : " << list.begin()->is_linked() << std::endl;
std::cout << "cloned_list[0].is_linked() : " << cloned_list.begin()->is_linked() << std::endl;
//////////////////////////////////////////////
// Gonna find out who's naughty or nice:
auto nit = cloned_nodes.begin();
auto lit = cloned_list.begin();
while (nit != cloned_nodes.end() && lit != cloned_list.end()) {
assert(&(*nit++) == &(*lit++)); // this would fail if you didn't `reserve()` the vector up front
}
//////////////////////////////////////////////
// now, if you really want you can do
cloned_list.clear();
// after which the simplest thing to do would be `cloned_nodes.clear()`, but let's be very precise:
cloned_nodes.erase(std::remove_if(
cloned_nodes.begin(), cloned_nodes.end(),
[](my_class const& v) { return !v.is_linked(); }),
cloned_nodes.end());
}
In fact, here's a version that puts the cloned nodes right there in the same vector as the source nodes, for fun: Live On Coliru too.