Triple map including 2 keys - c++

I have a structure containing 3 fields, two ints (let's call them A and B) and a bool (C).
I want to create a sort of array of that struct and be able to access it through any of the keys (A or B), getting the hole object (with A, B and C) in return.
I won't need to do something like "getting all the object for which the bool is true", if that makes any difference.
Obviously, both key are unique and the bool can't be, but I thought I'd mention it for the sake of clarity.
If there was no A or B, it would be a simple std::map<int, bool>.
The only solution I currently see is to make a wrapper containing 2 sets and a vector.
Is there any way to make my life easier?
NB: It will contain at most a hundred tuples, so performance should not be an issue. Linear access is acceptable.
To make it even clearer, here is what I'd like to be able to do:
foobar<int, int, bool> array; // or something along those lines
array.add(1, 101, true);
array.add(2, 102, false);
array.getA(1); // returns first object
array.getA(2); // returns second object
array.getB(102); // returns second object again

I believe what you're looking for is boost::multi_index. It'll allow you to declare a container with multiple indices.
struct MultiIDStruct
{
size_t idA;
size_t idB;
std::string name;
};
namespace mul = boost::multi_index;
boost::multi_index_container< MultiIDStruct,
mul::indexed_by<
mul::ordered_unique< mul::member< MultiIDStruct, size_t, &MultiIDStruct::idA > >,
mul::ordered_unique< mul::member< MultiIDStruct, size_t, &MultiIDStruct::idB > >
> > data;
(Used namespace "shortcut" as per Rapptz suggestion)
For example here you have a multi_index container of MultiIDStruct for which there are two unique orderings, one on idA (which is a member of MultiIDStruct) and a second on idB (which is also a member).
The template parameters seem like a handful at first but they're not so bad once you understand how they work.

The suggestion to split it up into two maps is certainly a bit simpler, but if you want some more flexibility and can use C++11 for features like std::tuple, you might try something of the form:
#include <iostream>
#include <map>
#include <tuple>
template <typename T1, typename T2, typename T3>
class foobar
{
public:
void add(T1 t1, T2 t2, T3 t3)
{
m1[t1] = std::make_tuple(t1, t2, t3);
m2[t2] = std::make_tuple(t1, t2, t3);
}
std::tuple<T1,T2,T3> getA(T1 t1)
{
return m1[t1];
}
std::tuple<T1,T2,T3> getB(T2 t2)
{
return m2[t2];
}
private:
std::map<T1,std::tuple<T1,T2,T3>> m1;
std::map<T2,std::tuple<T1,T2,T3>> m2;
};
int main()
{
foobar<int, int, bool> array; // or something along those lines
array.add(1, 101, true);
array.add(2, 102, false);
auto res1 = array.getA(1); // returns first object
auto res2 = array.getA(2); // returns second object
auto res3 = array.getB(102); // returns second object again
std::cout << std::get<0>(res1) << std::endl;
std::cout << std::get<1>(res2) << std::endl;
std::cout << std::get<2>(res3) << std::endl;
return 0;
}
A working example gives the output 1, 102, 0 (false).

I know I didn't give the detail implementation. But I'm just suggesting a logic with two maps. What's wrong with it? Why do I get downvoted?
struct s
{
int i;
int j;
bool b;
};
std::map<int, int> mapA;
std::map<int, s> mapB;
const s& getA(int i)
{
return mapB[mapA[i]];
}
const s& getB(int j)
{
return mapB[j];
}
void add(int i, int j, bool b)
{
s p;
p.i=i;
p.j=j;
p.b=b;
mapB[j]=p;
mapA[i]=j;
}

Having the same problem, and a different solution!
Have two hash functions on A and B, giving h1(A) and h2(B) such, that they do not give equal values. Example:
uint32_t A;
uint32_t B;
uint64_t hashA(uint32_t value)
{ return ((uint64_t)value) << 32; }
uint64_t hashB(uint32_t value)
{ return (uint64_t)value; }
Put all your stuff into std::map so that hashA and hashB have the same value for bool. Access it with either hashA or hashB.
Example: A = 0x10000001, B = 0x20000002, C = true
hashA(A): 0x1000000100000000
hashB(B): 0x0000000020000002
map:
0x1000000100000000 -> true
0x0000000020000002 -> true

Related

unordered_set of shared_ptr does not find equivalent objects it has stored

I have a class that stores a std::vector of stuff. In my program, I create a std::unordered_set of std::shared_ptr to objects of this class (see code below). I defined custom functions to compute hashes and equality so that the unordered_set "works" with the objects instead of the pointers. This means: Two different pointers to different objects that have the same content should be treated as equal, let's call it "equivalent".
So far everything worked as expected but now I stumbled across a strange behaviour: I add a pointer to an object to the unordered_set and create a different pointer to a different object with the same content. As said I would expect that my_set.find(different_object) would return a valid iterator to the equivalent pointer stored in the set. But it doesn't.
Here is a minimal working code example.
#include <boost/functional/hash.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <unordered_set>
#include <vector>
class Foo {
public:
Foo() {}
bool operator==(Foo const & rhs) const {
return bar == rhs.bar;
}
std::vector<int> bar;
};
struct FooHash {
size_t operator()(std::shared_ptr<Foo> const & foo) const {
size_t seed = 0;
for (size_t i = 0; i < foo->bar.size(); ++i) {
boost::hash_combine(seed, foo->bar[i]);
}
return seed;
}
};
struct FooEq {
bool operator()(std::shared_ptr<Foo> const & rhs,
std::shared_ptr<Foo> const & lhs) const {
return *lhs == *rhs;
}
};
int main() {
std::unordered_set<std::shared_ptr<Foo>, FooHash, FooEq> fooSet;
auto empl = fooSet.emplace(std::make_shared<Foo>());
(*(empl.first))->bar.emplace_back(0);
auto baz = std::make_shared<Foo>();
baz->bar.emplace_back(0);
auto eqFun = fooSet.key_eq();
auto hashFun = fooSet.hash_function();
if (**fooSet.begin() == *baz) {
std::cout << "Objects equal" << std::endl;
}
if (eqFun(*fooSet.begin(), baz)) {
std::cout << "Keys equal" << std::endl;
}
if (hashFun(*fooSet.begin()) == hashFun(baz)) {
std::cout << "Hashes equal" << std::endl;
}
if (fooSet.find(baz) != fooSet.end()) {
std::cout << "Baz in fooSet" << std::endl;
} else {
std::cout << "Baz not in fooSet" << std::endl;
}
return 0;
}
Output
Objects equal
Keys equal
Hashes equal
And here is the problem:
Baz not in fooSet
What am I missing here? Why does the set not find the equivalent object?
Possibly of interest: I played around with this and found that if my class stores a plain int instead of a std::vector, it works. If I stick to the std::vector but change my constructor to
Foo(int i) : bar{i} {}
and initialize my objects with
std::make_shared<Foo>(0);
it also works. If I remove the whole pointer stuff, It breaks as std::unordered_set::find returns constant iterators and thus modification of objects in the set cannot be done (this way). However, none of these changes is applicable in my real program, anyway.
I compile with g++ version 7.3.0 using -std=c++17
You can't modify an element of a set (and expect the set to work). Because you have provided FooHash and FooEq which inspect the referent's value, that makes the referent part of the value from the point of view of the set!
If we change the initialisation of fooSet to set up the element before inserting it, we get the result you want/expect:
std::unordered_set<std::shared_ptr<Foo>, FooHash, FooEq> fooSet;
auto e = std::make_shared<Foo>();
e->bar.emplace_back(0); // modification is _before_
fooSet.insert(e); // insertion
Looking up the object in the set depends on the hash value not changing. If we really need to modify a member after it has been added, we need to remove it, make the changes, then add the modified object - see Yakk's answer.
To avoid running into issues like this, it may be safer to use std::shared_ptr<const Foo> as elements, which will prevent modification of the pointed-at Foo through the set (although you're still responsible for the use of any non-const pointers you may also have).
Any operation such that the hash or == result of an element in an unordered_set violates the rules of unordered_set is bad; the result is undefined behavior.
You changed the result of a hash of an element in an unordered_set, because your elements are shared pointers, but their hash and == is based off of the value pointed to. And your code changes the value pointed to.
Make all std::shared_ptr<Foo> in your code std::shared_ptr<Foo const>.
This includes the equals and hash code and unordered set code.
auto empl = fooSet.emplace(std::make_shared<Foo>());
(*(empl.first))->bar.emplace_back(0);
this code is right out, and it will (afterwards) fail to compile, as is safe.
If you want to mutate an element in a fooSet,
template<class C, class It, class F>
void mutate(C& c, It it, F&& f) {
auto e = *it->first;
f(e); // do this before erasing, more exception-safe
auto new_elem = std::make_shared<decltype(e)>(std::move(e));
c.erase(it);
c.insert( new_elem ); // could throw, but hard to avoid.
}
now the code reads:
auto empl = fooSet.emplace(std::make_shared<Foo>());
mutate(fooSet, empl.first, [&](auto&& elem) {
elem.emplace_back(0);
});
mutate copies an element out, removes the pointer from the set, calls the function on it, then reinserts it back into the fooSet.
Of course in this case it is dumb; we just put it in and now we take it out mutate it and put it back.
But in a more general case it will be less dumb.
Here you add an object and it's stored using its current hash value.
auto empl = fooSet.emplace(std::make_shared<Foo>());
Here you change the hash value:
(*(empl.first))->bar.emplace_back(0);
The set now has an object stored using the old/wrong hash value. If you need to change anything in an object that affects its hash value, you need to extract the object, change it and re-insert it. If all mutable members of the class are used to calculate the hash value, make it a set of <const Foo> instead.
To make future declarations of sets of shared_ptr<const Foo> easier, you may also extend the std namespace with your specializations.
class Foo {
public:
Foo() {}
size_t hash() const {
size_t seed = 0;
for (auto& b : bar) {
boost::hash_combine(seed, b);
}
return seed;
}
bool operator==(Foo const & rhs) const {
return bar == rhs.bar;
}
std::vector<int> bar;
};
namespace std {
template<>
struct hash<Foo> {
size_t operator()(const Foo& foo) const {
return foo.hash();
}
};
template<>
struct hash<std::shared_ptr<const Foo>> {
size_t operator()(const std::shared_ptr<const Foo>& foo) const {
/* A version using std::hash<Foo>:
std::hash<Foo> hasher;
return hasher(*foo);
*/
return foo->hash();
}
};
template<>
struct equal_to<std::shared_ptr<const Foo>> {
bool operator()(std::shared_ptr<const Foo> const & rhs,
std::shared_ptr<const Foo> const & lhs) const {
return *lhs == *rhs;
}
};
}
With that in place, you can simply declare your unordered_set like this:
std::unordered_set<std::shared_ptr<const Foo>> fooSet;
which now is the same as declaring it like this:
std::unordered_set<
std::shared_ptr<const Foo>,
std::hash<std::shared_ptr<const Foo>>,
std::equal_to<std::shared_ptr<const Foo>>
> fooSet;

Variadic arguments and function pointers vector

I'm facing an almost-logical problem while working on C++11.
I have a class I have to plot (aka draw a trend) and I want to exclude all the points which do not satisfy a given condition.
The points are of the class Foo and all the conditional functions are defined with the signature bool Foo::Bar(Args...) const where Args... represents a number of parameters (e.g. upper and lower limits on the returned value).
Everything went well up to the moment I wished to apply a single condition to the values to plot. Let's say I have a FooPlotter class which has something like:
template<class ...Args> GraphClass FooPlotter::Plot([...],bool (Foo::*Bar)(Args...), Args... args)
Which will iterate over my data container and apply the condition Foo::*Bar to all the elements, plotting the values which satisfy the given condition.
So far so good.
At a given point I wanted to pass a vector of conditions to the same method, in order to use several conditions to filter data.
I first created a class to contain everything I need to have later:
template<class ...Args> class FooCondition{
public:
FooCondition(bool (Foo::*Bar)(Args...) const, Args... args)
{
fCondition = Bar;
fArgs = std::make_tuple(args);
}
bool operator()(Foo data){ return (data.*fCondition)(args); }
private:
bool (Foo::*fCondition)(Args...) const;
std::tuple<Args...> fArgs;
};
Then I got stuck on how to define a (iterable) container which can contain FooCondition objects despite them having several types for the Args... arguments pack.
The problem is that some methods have Args... = uint64_t,uint_64_t while others require no argument to be called.
I digged a bit on how to handle this kind of situation. I tried several approaches, but none of them worked well.
For the moment I added ignored arguments to all the Bar methods, uniformising them and working-around the issue, but I am not really satisfied!
Has some of you an idea on how to store differently typed FooCondition objects in an elegant way?
EDIT: Additional information on the result I want to obtain.
First I want to be able to create a std::vector of FooCondition items:
std::vector<FooCondition> conditions;
conditions.emplace_back(FooCondition(&Foo::IsBefore, uint64_t timestamp1));
conditions.emplace_back(FooCondition(&Foo::IsAttributeBetween, double a, double b));
conditions.emplace_back(FooCondition(&Foo::IsOk));
At this point I wish I can do something like the following, in my FooPlotter::Plot method:
GraphClass FooPlotter::Plot(vector<Foo> data, vector<FooCondition> conditions){
GraphClass graph;
for(const auto &itData : data){
bool shouldPlot = true;
for(const auto &itCondition : conditions){
shouldPlot &= itCondition(itData);
}
if(shouldPlot) graph.AddPoint(itData);
}
return graph;
}
As you can argue the FooCondition struct should pass the right arguments to the method automatically using the overloaded operator.
Here the issue is to find the correct container to be able to create a collection of FooCondition templates despite the size of their arguments pack.
It seems to me that, with FooCondition you're trying to create a substitute for a std::function<bool(Foo *)> (or maybe std::function<bool(Foo const *)>) initialized with a std::bind that fix some arguments for Foo methods.
I mean... I think that instead of
std::vector<FooCondition> conditions;
conditions.emplace_back(FooCondition(&Foo::IsBefore, uint64_t timestamp1));
conditions.emplace_back(FooCondition(&Foo::IsAttributeBetween, double a, double b));
conditions.emplace_back(FooCondition(&Foo::IsOk));
you should write something as
std::vector<std::function<bool(Foo const *)>> vfc;
using namespace std::placeholders;
vfc.emplace_back(std::bind(&Foo::IsBefore, _1, 64U));
vfc.emplace_back(std::bind(&Foo::IsAttributeBetween, _1, 10.0, 100.0));
vfc.emplace_back(std::bind(&Foo::IsOk, _1));
The following is a simplified full working C++11 example with a main() that simulate Plot()
#include <vector>
#include <iostream>
#include <functional>
struct Foo
{
double value;
bool IsBefore (std::uint64_t ts) const
{ std::cout << "- IsBefore(" << ts << ')' << std::endl;
return value < ts; }
bool IsAttributeBetween (double a, double b) const
{ std::cout << "- IsAttrributeBetwen(" << a << ", " << b << ')'
<< std::endl; return (a < value) && (value < b); }
bool IsOk () const
{ std::cout << "- IsOk" << std::endl; return value != 0.0; }
};
int main ()
{
std::vector<std::function<bool(Foo const *)>> vfc;
using namespace std::placeholders;
vfc.emplace_back(std::bind(&Foo::IsBefore, _1, 64U));
vfc.emplace_back(std::bind(&Foo::IsAttributeBetween, _1, 10.0, 100.0));
vfc.emplace_back(std::bind(&Foo::IsOk, _1));
std::vector<Foo> vf { Foo{0.0}, Foo{10.0}, Foo{20.0}, Foo{80.0} };
for ( auto const & f : vf )
{
bool bval { true };
for ( auto const & c : vfc )
bval &= c(&f);
std::cout << "---- for " << f.value << ": " << bval << std::endl;
}
}
Another way is avoid the use of std::bind and use lambda function instead.
By example
std::vector<std::function<bool(Foo const *)>> vfc;
vfc.emplace_back([](Foo const * fp)
{ return fp->IsBefore(64U); });
vfc.emplace_back([](Foo const * fp)
{ return fp->IsAttributeBetween(10.0, 100.0); });
vfc.emplace_back([](Foo const * fp)
{ return fp->IsOk(); });
All of the foo bar aside you just need a class with a method which can be implemented to satisfy the plot.
Just add a Plot method on the class which accepts the node and perform the transformation and plotting in the same step.
You need not worry about args when plotting because each function knows what arguments it needs.
Thus a simple args* will suffice and when null no arguments, therein each arg reveals it's type and value or can be assumed from the function invocation.

Data controlled programs in c++

Not to sure how to name this question because the problem itself is looking for a construct of which I don´t know its name.
The problem is I am dealing with programs whose control flow depends greatly of data.
For example I created a MIPS simulator which implemented a list of more than 50 instructions, each implemented on its own and everything governed by a huge switch case
switch (function){ //Function is an int, each function (eg SLL) is
case 0: //associated with one
if (state->debug_level > 0){
fprintf(state->debug_out, "SLL\n");
}
step_err = SLL(state, rs, rt, rd, sa);
break;
case 2:
if (state->debug_level > 0){
fprintf(state->debug_out, "SRL\n");
}
step_err = SRL(state, rs, rt, rd, sa);
break;
case 3:
if (state->debug_level > 0){
fprintf(state->debug_out, "SRA\n");
}
//
I have been told that this could have been implemented using function pointers, but to do so what I am looking for is a way of relating data of any kind, say a string to other data, say an integer. I am aware of maps but wouldn't want to push back each pair. I am looking for some sort of array like syntax I think if seen before which might look something similar to this:
¿type? function_codes[]{
0, "SLL";
2, "SRL";
3, "SRA";
...
}
I am not looking for a solution to this problem but a generic approach to introducing quick relationships between data and using this to modify control flow.
EDIT AFTER ANSWERS
What I was actually looking for but I didnt know was indeed maps but in particular its initialization syntax similar to an array (see accepted answer). This used with function pointers did the required job.
As you guessed, function pointers are in fact a good way to do this. Since you specify that you don't want to use a Map, this is how you would implement your integer-based function dispatch using an array of function pointers. Note that since I don't know the type signature of your MIPS functions (SLL, SRL, etc.) I've used dummy placeholder type names.
typedef ret_t (*mips_func)(arg1_t, arg2_t, arg3_t, arg4_t, arg5_t);
mips_func function_codes[] = {
&SLL,
&SRL,
&SRA,
...
};
//...Later, in the part of your code that used to contain the big switch statement
step_err = (*function_codes[function])(state, rs, rt, rd, sa);
The syntax &SLL gets a pointer to the function SLL, which I assume is already in scope because you can call it directly from your switch statement.
Note that this assumes the numeric codes for the functions are a continuous sequence of integers from 0 to [max code value]. If some numeric codes are unused, then you will either need to leave explicit gaps in your array (by placing a NULL pointer in one or more entries) or use std::map<int, mips_func> so that you can use arbitrary non-continuous integer values as keys to functions. Fortunately, using a Map still doesn't require push_backing each element, since C++ now has initializer lists. The same code using a Map would look like this:
typedef ret_t (*mips_func)(arg1_t, arg2_t, arg3_t, arg4_t, arg5_t);
std::map<int, mips_func> function_codes = {
{0, &SLL},
{2, &SRL},
{4, &SRA},
...
};
//Using the Map looks exactly the same, due to its overloaded operator[]
step_err = (*function_codes[function])(state, rs, rt, rd, sa);
For simplify you can use associative containers. If the order is important then use std::map, or std::unordered_map in the other case.
And you can use syntax similar to the desired
std::map<size_t, std::string> codes_map = decltype(codes_map) {
{ 0, "val1" },
{ 1, "val2" }
};
You could group the data as static members w/ the same name across structs, then use templates to access them generically:
struct A { auto call() const { return "((1))"; }; static const char * name; };
struct B { auto call() const { return "{{2}}"; }; static const char * name; };
struct C { auto call() const { return "<<3>>"; }; static const char * name; };
// n.b. these `T...` have: `sizeof(T) == ... == sizeof(empty_struct)`
const char * A::name = "A";
const char * B::name = "B";
const char * C::name = "C";
boost::variant (and the soon to be implemented std::variant) implements a type-safe union, which provides a very clean and efficient way of using these structs as values:
#include <cstdio>
#include <vector>
#include <boost/variant.hpp>
int main()
{
std::vector<boost::variant<A, B, C>> letters{A{}, B{}, C{}, B{}, A{}};
auto visitor = [](auto x) { std::printf("%s(): %s\n", x.name, x.call()); };
for (auto var : letters) { boost::apply_visitor(visitor, var); }
}
Demo
It seems like you have two problems: the flow-control issue (dispatch) and the map issue (an implementation note). I get that the program flow is nonstatic and unknowable at compile-time… but so is the map static? For static maps I get a lot of mileage out of using a traits-ish approach to create a compile-time mapping. Here’s a quick example mapping file suffixes to Objective-C enum constants:
namespace objc {
namespace image {
template <std::size_t N> inline
constexpr std::size_t static_strlen(char const (&)[N]) { return N; }
template <NSBitmapImageFileType t>
struct suffix_t;
#define DEFINE_SUFFIX(endstring, nstype) \
template <> \
struct suffix_t<nstype> { \
static constexpr std::size_t N = static_strlen(endstring); \
static constexpr char const str[N] = endstring; \
static constexpr NSBitmapImageFileType type = nstype; \
};
DEFINE_SUFFIX("tiff", NSTIFFFileType);
DEFINE_SUFFIX("bmp", NSBMPFileType);
DEFINE_SUFFIX("gif", NSGIFFileType);
DEFINE_SUFFIX("jpg", NSJPEGFileType);
DEFINE_SUFFIX("png", NSPNGFileType);
DEFINE_SUFFIX("jp2", NSJPEG2000FileType);
template <NSBitmapImageFileType nstype>
char const* suffix_value = suffix_t<nstype>::str;
}
}
… see how that works? the nice part is that using it has no runtime overhead, which if your map is static, you can use something like that.
For dynamic flow-control and dispatch, function pointers work; that is what happens automatically if you use polymorphic classes and virtual functions but it seems like you have an architecture in place already that may not be amenable to being redone with such high-modernist architectural notions. I like c++11 lambdas as they solve like 90% of my problems in this arena. Perhaps you can elablrate (I will amend my answer)!
If you only have a small number of indices to support, from 0 to 50, you'll get the best performance if you put your function pointers in an array and not a map.
The syntax is also short:
#include <iostream>
#include <functional>
static void f0() {
std::cout << "f0\n";
}
static void f1() {
std::cout << "f1\n";
}
void main()
{
std::function<void()> f[2] = { f0, f1 };
f[0](); // prints "f0"
f[1](); // prints "f1"
}
Or, if you prefer classes over functions:
#include "stdafx.h"
#include <iostream>
class myfunc {
public:
virtual void run() abstract;
virtual ~myfunc() {}
};
class f0 : public myfunc {
public:
virtual void run() {
std::cout << "f0\n";
}
};
class f1 : public myfunc {
public:
virtual void run() {
std::cout << "f1\n";
}
};
void main()
{
myfunc* f[2] = { new f0(), new f1() };
f[0]->run(); // prints "f0"
f[1]->run(); // prints "f1"
for (int i = 0; i < sizeof(f) / sizeof(f[0]); ++i)
delete f[i];
}
Given some definitions
#include <iostream>
#include <iterator>
#include <algorithm>
#include <stdexcept>
#include <map>
using namespace std;
struct state{
int debug_level = 1;
const char* debug_out = "%s";
} s;
// some functions to call
void SLL(state& s, int, int, int, int){
cout << "SLL";
}
void SLR(state& s, int, int, int, int){
cout << "SLR";
}
void SLT(state& s, int, int, int, int){
cout << "SLT";
}
You can use a Map
auto mappedname2fn = map<string, delctype(SLL)*>{
{"SLL", SLL},
{"SLR", SLR}
};
// call a map function
mappedname2fn["SLR"](s, 1, 2, 3, 4);
If you don't want a map you can use a pre-sorted array for a binary search
Here's a binary search of an array of name, function pairs
template<typename P, int N, typename ...T>
auto callFn(P(&a)[N], string val, T&&... params){
auto it = lower_bound(a, a+N, make_pair(val, nullptr),
[](auto& p1, auto& p2){return p1.first < p2.first;});
if(it==(a+N) || val<it->first) throw logic_error("not found");
return it->second(forward<T>(params)...);
}
So you can set up an array and use that:-
// array sorted in alphabetical order for binary search to work
pair<string, decltype(SLL)*> name2fn[] = {
{"SLL", SLL},
{"SLR", SLR},
{"SLT", SLT}
};
void callFn(string name, state& s, int a, int b, int c, int d){
try{
callFn(name2fn, name, s, a, b, c, d);
}
catch(exception& e){
cout << e.what();
}
}
// call it
callFn("SLL", s, 1, 2, 3, 4);

Sorting just two elements using STL

Quite often I have two variables foo1 and foo2 which are numeric types. They represent the bounds of something.
A user supplies values for them, but like a recalcitrant musician, not necessarily in the correct order!
So my code is littered with code like
if (foo2 < foo1){
std::swap(foo2, foo1);
}
Of course, this is an idiomatic sort with two elements not necessarily contiguous in memory. Which makes me wonder: is there a STL one-liner for this?
I suggest to take a step back and let the type system do the job for you: introduce a type like Bounds (or Interval) which takes care of the issue. Something like
template <typename T>
class Interval {
public:
Interval( T start, T end ) : m_start( start ), m_end( end ) {
if ( m_start > m_end ) {
std::swap( m_start, m_end );
}
}
const T &start() const { return m_start; }
const T &end() const { return m_end; }
private:
T m_start, m_end;
};
This not only centralizes the swap-to-sort code, it also helps asserting the correct order very early on so that you don't pass around two elements all the time, which means that you don't even need to check the order so often in the first place.
An alternative approach to avoid the issue is to express the boundaries as a pair of 'start value' and 'length' where the 'length' is an unsigned value.
No, but when you notice you wrote the same code twice it's time to write a function for it:
template<typename T, typename P = std::less<T>>
void swap_if(T& a, T& b, P p = P()) {
if (p(a, b)) {
using std::swap;
swap(a, b);
}
}
 
std::minmax returns pair of smallest and largest element. Which you can use with std::tie.
#include <algorithm>
#include <tuple>
#include <iostream>
int main()
{
int a = 7;
int b = 5;
std::tie(a, b) = std::minmax({a,b});
std::cout << a << " " << b; // output: 5 7
}
Note that this isn't the same as the if(a < b) std::swap(a,b); version. For example this doesn't work with move-only elements.
if the data type of your value that you're going to compare is not already in c++. You need to overload the comparison operators.
For example, if you want to compare foo1 and foo2
template <class T>
class Foo {
private:
int value; // value
public:
int GetValue() const {
return value;
}
};
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.GetValue() < rhs.GetValue());
}
If your value is some type of int, or double. Then you can use the std::list<>::sort member function.
For example:
std::list<int> integer_list;
int_list.push_back(1);
int_list.push_back(8);
int_list.push_back(9);
int_list.push_back(7);
int_list.sort();
for(std::list<int>::iterator list_iter = int_list.begin(); list_iter != int_list.end(); list_iter++)
{
std::cout<<*list_iter<<endl;
}

Reversing the content of a set in C++

I want to reverse the contents of a std::set(Not just iterating theough it in reverse, but reversing the contents iteslf). I found that std::set takes the compare as a function object for its constructor. Hence I came up with the following code to do the same:
#include <set>
using namespace std;
struct Comparator
{
Comparator(bool g) : m_g(g){}
bool operator()(int i1, int i2) const
{
if(m_g)
{
return i1>i2;
}
return i1<i2;
}
bool m_g;
};
int main(int argc, char** argv)
{
Comparator l(false);
set<int,Comparator> a(l);
a.insert(1);
a.insert(2);
a.insert(3);
Comparator g(true);
set<int,Comparator> b(g);
copy(a.begin(), a.end(), inserter(b, b.begin()));
a = b;
return 0;
}
This seems to work in VC9. But is this code correct? My doubt arises due to the fact my Comparator has state associated with it. Are comparators are allowed to have states?
Yes, that's legal. Consider that if the comparator was not allowed to have state, there would be no point in allowing you to pass a comparator as a constructor parameter. :)
As long as the comparator provides a strict weak ordering, it's fine (which, among other things, means that it has to be consistent. You can't change the state of it halfway through, so that it orders elements differently)
It's fine, but it's needlessly complex.
You can just use std::less (the default value for that template parameter!) or std::greater from the standard library. They are provided by <functional>.
A more generic solution. boost::assign and c++11 just for convenience (and the funny auto reverse)
# include <iostream>
# include <set>
# include <boost/assign.hpp>
using namespace boost::assign;
template <typename CL , typename Pred>
struct revPred {
revPred (Pred pred) : pred_(pred) {}
bool operator()(const CL & a, const CL& b)
{
return pred_(b,a);
}
Pred pred_;
};
template <typename CL , typename Pred, typename alloc>
inline
std::set<CL,revPred<CL,Pred>,alloc> reverseSet(const std::set<CL,Pred,alloc> & set) {
std::set<CL,revPred<CL,Pred>,alloc> res(revPred<CL,Pred>(set.key_comp()));
std::copy(set.begin(), set.end(), std::inserter(res, res.begin()));
return res;
}
int main()
{
std::set<int> s; s += 0 , 1 , 2 , 3;
std::for_each(s.begin(), s.end(), [](int x) { std::cout << x << " "; });
std::cout << std::endl;
auto reverse = reverseSet(s);
std::for_each(reverse.begin(), reverse.end(), [](int x) { std::cout << x << " "; });
std::cout << std::endl;
return 0;
}
There is nothing wrong with your code.
And there is nothing wrong with comparators having state.
This is ok since your comparison doesn't change dynamically and it does provide strict weak ordering.
However, if you're doing this so that the type of the set is the same even when the order changes, I might suggest an alternate idea. Instead of this comparison, you use two different set types with std::less and std::greater and use an iterator interface like the standard library does, rather than a container interface that depends on all the template parameters.
And finally as noted in the answer from #parapura rajkumar you should use the iterator pair constructor rather than std::copy:
// Assuming my other comments don't apply, modify as needed if they do:
Comparator g(true);
set<int, Comparator> b(a.rbegin(), a.rend(), g);
You don't need to provide a function object which maintains state. Just use a normal function and it should do the job. Pls. don't mind for the use of lambda. Used as a short cut to do the printing.
typedef bool (*Cmp)(int x, int y);
bool Compare(int x, int y)
{
return x < y;
}
bool CompareR(int x , int y)
{
return !Compare(x, y);
}
void SetReverse()
{
std::set<int, Cmp> s1(Compare);
s1.insert(1);
s1.insert(3);
s1.insert(2);
s1.insert(4);
std::for_each(s1.begin(), s1.end(), [](int x) { std::cout << x << "\n"; });
std::set<int, Cmp> s2(CompareR);
s2.insert(s1.begin(), s1.end());
std::for_each(s2.begin(), s2.end(), [](int x) { std::cout << x << "\n"; });
s1 = s2;
}
If you have a large enough set you have already paid the O(NlogN) penalty to make a balanced binary tree. A blind insert into the destination set, you will have to pay the penalty again.
Consider using one of these insert overload.
void insert ( InputIterator first, InputIterator last );
iterator insert ( iterator position, const value_type& x );
The range insert has linear complexity if [ first , last ) are sorted already.