There are 4 things that I was wondering if I could do in C++ with macros or inline functions.
(1) postfix operations in normal types (int, char, etc)
int n = 5;
printf("%d\n", n!)
output: 120
(2) infix operations in normal types (int, char, etc)
int a = 20, b = 10
if(b|a) printf("%d is divisor of %d\n", b, a);
// the symbol | means "divides", this is like (a % b == 0)
output: 10 is divisor of 20
(3) sequential operations in common types
int a = 5, b = 15, c = 20;
if(a < b < c) printf("true\n");
output: false
//doesn't matter if it's made with symbols or words
(4) for each (not using for(auto...) C++11) and
it may different for lists, vectors, etc...
vector<int> v = {1,3,5,7};
foreach(e : v) printf("%d ", e);
output: 1 3 5 7
That's it. Is it possible to make any of these macros in C++ ?
Thanks !
PS: the symbols dont need to be | or !. Since they work, they can be # or $ or anything. The idea is to be postfix (a#), infix(a#b), sequential (a#b#c) and the foreach may be different, just to be smaller than a normal for is ok
No, you could make a function like int factorial(int x) and use printf( .. factorial(... but you cannot create operators like this from a macro.
No, | is already an operator (bitwise or).
This is the case anyway,. so what are you trying to do?
Why? Just use auto?
I think you are facing the X Y problem. There is likely no reason to make any of these a macro.
If you're willing to give your operators a textual name, you can certainly do 1, 2, 3. I believe 4 is what templates are for, i.e., take std::vector with T as a template parameter. You can inline any of these functions. Note that this code will probably not pass a code review, it's just here for your experimenting.
#include <iostream>
#include <boost/optional.hpp> // change to <optional> if you have it
#define OPTIONAL_NS boost // change it to std if above is <optional>
using namespace std;
struct Factorial {};
#define FACTORIAL * Factorial() /* choose operator based on associativity and precedence */
int operator*( const int n, const Factorial& )
{
return n == 0 ? 1 : (n - 1) FACTORIAL * n;
}
template<typename T, typename U>
struct StoreOne
{
const U m_a;
StoreOne( const U& a )
: m_a( a )
{
}
operator bool() const // do this only if U can be casted to bool
{
return m_a;
}
};
struct Divides {};
#define DIVIDES * Divides() *
StoreOne<Divides, int> operator*( const int a, const Divides& )
{
return a;
}
bool operator*( const StoreOne<Divides, int> da, const int b )
{
return b % da.m_a == 0;
}
struct Gt {};
#define GT < Gt() <
StoreOne<Gt, OPTIONAL_NS::optional<int> > operator<( const OPTIONAL_NS::optional<int> a, const Gt& )
{
return OPTIONAL_NS::optional<int>( a );
}
OPTIONAL_NS::optional<int> operator<( const StoreOne<Gt, OPTIONAL_NS::optional<int> >& ga, const int b )
{
if ( ga.m_a )
{
if ( *ga.m_a < b )
{
return OPTIONAL_NS::optional<int>( b );
}
}
return OPTIONAL_NS::optional<int>();
}
template<typename T>
void printVector( const std::vector<T>& v )
{
for ( const T& t : v )
{
cout << t << endl;
}
}
int main() {
cout << endl << "Factorial: " << ( 5 FACTORIAL );
cout << endl << "Divides: " << ( 5 DIVIDES 120 ? "Success" : "Failed" );
cout << endl << "Greater-than 1: " << ( 3 GT 4 GT 5 ? "Success" : "Failed" );
cout << endl << "Greater-than 2: " << ( !( 3 GT 4 GT 3 ) ? "Success" : "Failed" );
cout << endl << "Greater-than 3: " << ( !( 5 GT 4 GT 5 ) ? "Success" : "Failed" );
cout << endl;
std::vector<int> v;
v.push_back( 1 );
v.push_back( 2 );
v.push_back( 3 );
printVector( v );
}
Related
I got a class Matrix with a member std::list<Element> listMatrix;. Element is a a class with 3 int members line, column, value. I save in the list, elements of a matrix that are not 0 by saving the line, column and the value of the respectively element. I want to overload the operator [][] so I can do something like Matrix a; a[2][3] = 5;. I know you can't overload [][] directly.
Do overload Element& operator()(int, int) (and the const variant) so you can write
matrix(2, 3) = 5;
If you absolutely need the [2][3] syntax, you'd need to define a proxy class so matrix[2] return a proxy value and proxy[3] return the desired reference. But it comes with a lot of problems. The basic idea would be:
class naive_matrix_2x2
{
int data[4];
struct proxy
{
naive_matrix_2x2& matrix;
int x;
int& operator[](int y) { return matrix.data[x*2+y]; }
};
public:
proxy operator[](int x) { return {*this, x}; }
};
Full demo: https://coliru.stacked-crooked.com/a/fd053610e56692f6
The list is not a suitable container for using the subscript operator because it has no direct access to its elements without moving an iterator through the list.
So the operator will be inefficient.
It is better to use the standard container std::vector that already has the subscript operator.
Nevertheless answering your question the operator can be defined the following way. You can add to the operators an exception then an index will point outside the list.
#include <iostream>
#include <list>
struct A
{
int x, y, z;
int & operator []( size_t n )
{
return n == 0 ? x : n == 1 ? y : z;
}
const int & operator []( size_t n ) const
{
return n == 0 ? x : n == 1 ? y : z;
}
};
struct B
{
std::list<A> lst;
A & operator []( size_t n )
{
auto it = std::begin( lst );
for ( ; n; n-- ) std::advance( it, 1 );
return *it;
}
const A & operator []( size_t n ) const
{
auto it = std::begin( lst );
for ( ; n; n-- ) std::advance( it, 1 );
return *it;
}
};
int main()
{
B b = { { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } } };
std::cout << b[0][0] << '\n';
std::cout << b[0][1] << '\n';
std::cout << b[0][2] << '\n';
b[2][1] += 20;
std::cout << b[2][1] << '\n';
}
The program output is
1
2
3
28
Consider this simple class storing a value and a time.
class A
{
public:
boost::posix_time::ptime when;
double value;
};
Depending on the context, I need to compare two instances of A by value or by time (and/or store them in set/map, sometimes sorted by value, sometimes by time).
Providing operator< will be confusing, because you can't tell if it will compare by value or by time.
Now, what's the best strategy?
Is it possible to provide an operator< taking a parameter? (would be used as a <(ByTime) b)?
Should I have a lowerThan (comparing values) method and a earlierThan (comparing time) method taking the right operand as parameter? But then, what would be the best practice to handle <, <=, >, >=, ==, !=, should I have one method for each comparator? Or may they take parameters (like bool isLower(bool strict, const A& right) const, bool isGreater(bool strict, const A& right) const, bool isEarlier(bool strict, const A& right) const, bool isLater(bool strict, const A& right) const...
What would be the best practice?
IMHO the most versatile way is a 2-step process:
make ADL getters.
write comparison concepts in terms of those getters.
example:
#include <boost/date_time.hpp>
#include <set>
#include <vector>
#include <algorithm>
class A
{
public:
boost::posix_time::ptime when;
double value;
};
// get the 'when' from an A
auto get_when(A const& a) -> boost::posix_time::ptime
{
return a.when;
}
// get the 'when' from a ptime (you could put this in the boost::posix_time namespace for easy ADL
auto get_when(boost::posix_time::ptime t) -> boost::posix_time::ptime
{
return t;
}
// same for the concept of a 'value'
auto get_value(A const& a) -> double
{
return a.value;
}
auto get_value(double t) -> double
{
return t;
}
// compare any two objects by calling get_when() on them
struct increasing_when
{
template<class L, class R>
bool operator()(L&& l, R&& r) const
{
return get_when(l) < get_when(r);
}
};
// compare any two objects by calling get_value() on them
struct increasing_value
{
template<class L, class R>
bool operator()(L&& l, R&& r) const
{
return get_value(l) < get_value(r);
}
};
void example1(std::vector<A>& as)
{
// sort by increasing when
std::sort(begin(as), end(as), increasing_when());
// sort by increasing value
std::sort(begin(as), end(as), increasing_value());
}
int main()
{
// same for associative collections
std::set<A, increasing_when> a1;
std::set<A, increasing_value> a2;
}
update:
If you want, you can templatise the comparison:
template<class Comp>
struct compare_when
{
template<class L, class R>
bool operator()(L&& l, R&& r) const
{
return comp(get_when(l), get_when(r));
}
Comp comp;
};
using increasing_when = compare_when<std::less<>>;
using decreasing_when = compare_when<std::greater<>>;
to use the comparison directly in code:
auto comp = compare_when<std::greater<>>();
if (comp(x,y)) { ... }
Reacting to UKMonkey comment, would defining what I understand could be named "comparator classes" be a good approach/practice?
class A
{
public:
boost::posix_time::ptime when;
double value;
const boost::posix_time::ptime& getTime() const { return when; }
double getValue() const { return value; }
};
template <typename T>
class CompareBy
{
public:
CompareBy( const A& a, T (A::*getter)() const ) : a(a), getter(getter)
{}
bool operator<( const CompareBy& right ) const
{
return (a.*getter)() < (right.a.*getter)();
}
// you may also declare >, <=, >=, ==, != operators here
private:
const A& a;
T (A::*getter)() const;
};
class CompareByTime : public CompareBy<const boost::posix_time::ptime&>
{
public:
CompareByTime(const A& a) : CompareBy(a, &A::getTime)
{
}
};
class CompareByValue : public CompareBy<double>
{
public:
CompareByValue( const A& a ) : CompareBy(a, &A::getValue)
{
}
};
struct byTime_compare {
bool operator() (const A& lhs, const A& rhs) const {
return CompareByTime(lhs) < CompareByTime(rhs);
}
};
int main()
{
A a, b;
...
if (CompareByValue(a) < CompareByValue(b))
{
...
}
std::set<A, byTime_compare> mySet;
}
short answer: don't
I explained why in a comment, the main reason is, it introduces ambiguity in your code and reduces readability which is the opposite of what operators are meant to do. Just use different methods and provide ways to pick which one to use for this sort (like comparers). While I was typing this, people posted good examples of that, even some using a bit of metaprogramming.
however, for science, you kinda can. While you can't add a parameter to an operator (a binary operator is a binary operator, and there doesn't seem to be a syntax to add this third argument somewhere) you can make your operator mean different things in different contexts (c++ context, for a line of code or for a block delimited by '{}')
here done very quickly using construction/destruction order (similar implementation to a trivial lock with no consideration for thread safety):
the comparison looks like:
Thing::thingSortingMode(Thing::thingSortingMode::alternateMode), Thing{1, 2} < Thing{3, 4};
run this example online: http://cpp.sh/3ggrq
#include <iostream>
struct Thing {
struct thingSortingMode {
enum mode {
defaultMode,
alternateMode
};
mode myLastMode;
thingSortingMode(mode aMode) { myLastMode = Thing::ourSortingMode; Thing::ourSortingMode = aMode; std::cout << "\nmode: " << aMode << "\n"; }
~thingSortingMode() { Thing::ourSortingMode = myLastMode; std::cout << "\nmode: " << myLastMode << "\n";}
};
bool operator < (Thing another) {
switch (ourSortingMode) //I use an enum, to make the example more accessible, you can use a functor instead if you want
{
case thingSortingMode::alternateMode:
return myValueB < another.myValueB;
break;
default:
return myValueA < another.myValueA;
break;
}
}
static thingSortingMode::mode ourSortingMode;
int myValueA;
int myValueB;
};
Thing::thingSortingMode::mode Thing::ourSortingMode = Thing::thingSortingMode::defaultMode;
int main()
{
Thing a{1, 1}, b{0, 2}; // b < a in default mode, a < b in alternate mode
std::cout << (a < b); //false
{
Thing::thingSortingMode ctx(Thing::thingSortingMode::alternateMode);
std::cout << (a < b); //true
Thing::thingSortingMode(Thing::thingSortingMode::defaultMode), std::cout << (a < b), //false
Thing::thingSortingMode(Thing::thingSortingMode::alternateMode), std::cout << (a < b); //true
std::cout << (a < b); //true
}
std::cout << (a < b); //false
}
Note that this construction/destruction trick can manage any kind of contextual state, here is a richer example with 4 states and more nested contexts
run this example online: http://cpp.sh/2x5rj
#include <iostream>
struct Thing {
struct thingSortingMode {
enum mode {
defaultMode = 1,
alternateMode,
mode3,
mode4,
};
mode myLastMode;
thingSortingMode(mode aMode) { myLastMode = Thing::ourSortingMode; Thing::ourSortingMode = aMode; std::cout << "\nmode: " << myLastMode << " -> " << aMode << "\n"; }
~thingSortingMode() { std::cout << "\nmode: " << Thing::ourSortingMode << " -> " << myLastMode << "\n"; Thing::ourSortingMode = myLastMode; }
};
static thingSortingMode::mode ourSortingMode;
};
Thing::thingSortingMode::mode Thing::ourSortingMode = Thing::thingSortingMode::defaultMode;
int main()
{
Thing::thingSortingMode ctx(Thing::thingSortingMode::mode3);
{
Thing::thingSortingMode ctx(Thing::thingSortingMode::alternateMode);
{
Thing::thingSortingMode ctx(Thing::thingSortingMode::mode4);
{
Thing::thingSortingMode ctx(Thing::thingSortingMode::defaultMode);
std::cout << "end sub 3 (mode 1)\n";
}
std::cout <<
(Thing::thingSortingMode(Thing::thingSortingMode::alternateMode), "this is the kind of things that might behave strangely\n") <<
(Thing::thingSortingMode(Thing::thingSortingMode::defaultMode), "here both are printed in mode 2, but it's a direct consequence of the order in which this expression is evaluated\n"); //note though that arguments are still constructed in the right state
std::cout << "end sub 2 (mode 4). Not that we still pop our states in the right order, even if we screwed up the previous line\n";
}
std::cout <<
(Thing::thingSortingMode(Thing::thingSortingMode::alternateMode), "this on the other hand (mode 2)\n"),
std::cout <<
(Thing::thingSortingMode(Thing::thingSortingMode::defaultMode), "works (mode 1)\n"); //but pay attention to the comma and in which order things are deleted
std::cout << "end sub 1 (mode 2)\n";
}
std::cout << "end main (mode 3)\n";
}
output:
mode: 1 -> 3
mode: 3 -> 2
mode: 2 -> 4
mode: 4 -> 1
end sub 3 (mode 1)
mode: 1 -> 4
mode: 4 -> 1
mode: 1 -> 2
this is the kind of things that might behave strangely
here both are printed in mode 2, but it's a direct consequence of the order in which this expression is evaluated
mode: 2 -> 1
mode: 1 -> 4
end sub 2 (mode 4). Not that we still pop our states in the right order, even if we screwed up the previous line
mode: 4 -> 2
mode: 2 -> 2
this on the other hand (mode 2)
mode: 2 -> 1
works (mode 1)
mode: 1 -> 2
mode: 2 -> 2
end sub 1 (mode 2)
mode: 2 -> 3
end main (mode 3)
mode: 3 -> 1
Another approach, very simple: add template comparator functions to the A class makes it easy to do a comparison in the end and is really error prone:
#include <iostream>
#include <set>
using namespace std;
class A
{
public:
int when;
double value;
int getTime() const { return when; }
double getValue() const { return value; }
template<typename T>
bool isLower( T (A::*getter)() const,
bool strict,
const A& right ) const
{
if ( strict )
return ((*this).*getter)() < (right.*getter)();
else
return ((*this).*getter)() <= (right.*getter)();
}
template<typename T>
bool isGreater( T (A::*getter)() const,
bool strict,
const A& right ) const
{
if ( strict )
return ((*this).*getter)() > (right.*getter)();
else
return ((*this).*getter)() >= (right.*getter)();
}
template<typename T>
bool isEqual( T (A::*getter)() const,
const A& right ) const
{
return ((*this).*getter)() == (right.*getter)();
}
};
struct byTime_compare {
bool operator() (const A& lhs, const A& rhs) const {
return lhs.isLower( &A::getTime, true, rhs );
}
};
int main()
{
A a, b;
if ( a.isLower( &A::getValue, true, b ) ) // means a < b by value
{
// ...
}
std::set<A, byTime_compare> mySet;
}
std::string Breitensuche ( std::array < std::list<std::array<int, 2>>, 3> list, int von, int zu ,std::string weg)
{
std::list<int> markiert;
for ( auto &b : list.at(von) )
{
if ( b.at ( 0 ) == zu )
return
weg += std::to_string ( b.at ( 0 ) );
else
markiert.push_back ( b.at ( 0 ) );
}
for ( auto &a : markiert )
{
Breitensuche ( list, a.at ( 0 ), zu );
}
return "";
}
int main ( )
{
std::array < std::list<std::array<int, 2>>, 3> Adjazenzliste { { { { { 0, 5 } } }, { { { 0, 5 } }, { { 2, 7 } } }, { { { 1, 4 } } } } };
std::cout << Breitensuche ( Adjazenzliste, 1, 2 ,"");
system ( "Pause" );
}
I was trying to implement bridesearch of graphs with c++ with adjazenzlists.
the first part of the arrays which are hold in the list which are hold in arrays are the name of the node to which the startnode of the list has a connection and the second is the weight of the connection.
So basicly in this initalization there are 3 lists
0 -> 1
1 -> 0 -> 2
2 -> 1
In the function above a tried that first of all every element of the list gets checked and if its not the searched node it gets marked and after this the function gets called for every marked spot again and again, if its found it returns the nodename.
I am encountering problems with not getting into depthsearch cause if i do it recursive it will allways first check it deep and then do the next...
Furthemore i have problems to safe the "path" if its found and return it...
I hope you understand what i mean, sorry for my bad english
The Breadth-first search algorithm described on Wikipedia doesn't require recursiveness. The pseudocode talks about a queue q for the ordering and a set V (capital V) to make sure duplicate values get matched only once (which I have left out in the code below):
#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;
template<class T>
struct tree_item {
T value;
vector<tree_item<T>> children;
tree_item(T value) : value(value) {}
};
// constructs animated gif http://en.wikipedia.org/wiki/Breadth-first_search
tree_item<string> build_tree() {
tree_item<string> a("a"), b("b"), c("c"), d("d"), e("e"), f("f"), g("g"), h("h");
e.children = { h };
b.children = {d, e};
c.children = {f, g};
a.children = {b, c};
return a;
}
// implements "Algorithm" -> "Pseudocode" http://en.wikipedia.org/wiki/Breadth-first_search
template<class T>
bool find_breadth_first(const tree_item<T>& v, function<bool(const T&)> matcher, tree_item<T>& found_item) {
queue<const tree_item<T>*> q; // step 2
q.push(&v); // step 5
while(!q.empty()) { // step 6
auto t = q.front(); q.pop(); // step 7
cout << "currently visiting " << t->value << endl;
if(matcher(t->value)) { // step 8
cout << t->value << " is a match " << endl;
found_item = *t; return true; // step 9
}
for(auto& u : t->children) // step 11
q.push(&u); // step 15
}
return false; // step 19
}
int main() {
auto root = build_tree();
tree_item<string> match("no match");
auto matcher = [&](auto candidate) {
return candidate == "g";
};
auto found = find_breadth_first<string>(root, matcher, match);
if(found)
cout << "found: " << match.value << endl;
else
cout << "not found" << endl;
}
Output:
currently visiting a
currently visiting b
currently visiting c
currently visiting d
currently visiting e
currently visiting f
currently visiting g
g is a match
found: g
The functionality that I want is like:
std::vector<float> GetFuncVec(int N, FuncType type)
{
std::vector<float> fn(N);
float tmp = (N - 1) / 2.0;
switch (type) {
case SIN:
for (int i=0; i<N; ++i)
fn[i] = sin(M_PI * i / tmp);
break;
case SINC:
for (int i=0; i<N; ++i)
fn[i] = sin(M_PI * i / tmp) / (M_PI * i / tmp);
break;
...
}
return fn;
}
I find this unsatisfactory because there is a lot of code duplication. Looking around, I found the STL algorithm std::generate() which can fill a vector using a functor, which can have an increment member to play the role of i.
I see two potential routes. The first is to use a factory to initialize the functor. The problem with this method is code separation (above, the different cases are kept nicely together) and increased overheads with multiple new classes needed.
The second is to use lambda functions (which I have very little experience with). This is nice because I can define each function in a single line in the switch statement. But I don't see how I can avoid a scoping problem (the lambda function is not accessible outside the scope of the switch statement).
Is there a solution using lambda functions? What is the best option, from an efficiency viewpoint and from a readability viewpoint?
Maybe you want something like this...? (see it run here
#include <iostream>
#include <vector>
#include <cmath>
#include <functional>
enum Func { Sin, Sinc };
std::vector<float> f(int n, Func func)
{
std::vector<float> results(n);
float tmp = (n - 1) / 2.0;
int i;
std::function<float()> fns[] = {
[&] { return sin(M_PI * i / tmp); },
[&] { return sin(M_PI * i / tmp) / (M_PI * i / tmp); }
};
auto& fn = fns[func];
for (i=0; i<n; ++i)
results[i] = fn();
return results;
}
int main()
{
std::vector<float> x = f(10, Sin);
for (auto& v : x) std::cout << v << ' '; std::cout << '\n';
std::vector<float> y = f(10, Sinc);
for (auto& v : y) std::cout << v << ' '; std::cout << '\n';
}
Output:
0 0.642788 0.984808 0.866025 0.34202 -0.34202 -0.866025 -0.984808 -0.642788 -2.44929e-16
-nan 0.920725 0.705317 0.413497 0.122477 -0.0979816 -0.206748 -0.201519 -0.115091 -3.89817e-17
One option that may not be fast (there is indirection on each function call) but that would be a bit more flexible would be to create an std::map<FuncType, std::function<float(int,float)>>. You can't use std::generate() because you need the argument i to calculate the result, but writing your own is not that hard:
template <typename Iterator, typename Generator, typename Index, typename... Args>
void generate_i(Iterator first, Iterator last, Generator gen, Index i, Args... args)
{
while (first != last) {
*first = gen(i, args...);
++i;
++first;
}
}
Now that we have this, we need to populate a map of functors:
using FuncTypeFunction = std::function<float(int,float)>;
using FuncTypeFunctionMap = std::map<FuncType, FuncTypeFunction>;
FuncTypeFunctionMap create_functype_map()
{
FuncTypeFunctionMap functions;
functions[SIN] = [] (int i, float tmp) {
return sin(M_PI * i / tmp);
};
functions[SINC] = [] (int i, float tmp) {
return sin(M_PI * i / tmp) / (M_PI * i / tmp);
};
// ...
return functions;
}
FuncTypeFunctionMap const FuncTypeFunctions = create_functype_map();
(If you prefer you can use boost.assign to improve readability of this bit.)
And finally, we can use this map:
std::vector<float> GetFuncVec(int N, FuncType type)
{
std::vector<float> fn(N);
float tmp = (N - 1) / 2.0;
auto func = FuncTypeFunctions.find(type);
if (func != FuncTypeFunctions.end()) {
generate_i(fn.begin(), fn.end(), func->second, 0, tmp);
}
return fn;
}
Adding new functions only requires populating the map in create_functype_map(). Note that each iteration in the generate_i() loop is going to invoke the operator() on std::function, which will require a level of indirection to resolve the call, similar to the overhead of a virtual method invocation. This will cost a bit in terms of performance but may not be an issue for you.
(See a demo)
You may write a general class that will be used in standard algorithm std::iota
For example
#include <iostream>
#include <functional>
#include <vector>
#include <numeric>
class Value
{
public:
Value() : i( 0 ), fn( []( size_t i ) { return ( float )i; } ) {}
Value & operator ++() { ++i; return *this; }
operator float () const { return fn( i ); }
Value & operator =( std::function<float( size_t )> fn )
{
this->fn = fn;
return *this;
}
private:
size_t i;
std::function<float( size_t )> fn;
};
enum E { First, Second };
std::vector<float> f( size_t N, E e )
{
Value value;
float tmp = N / 2.0f;
switch( e )
{
case First:
value = [tmp] ( size_t i ) { return i * tmp; };
break;
case Second:
value = [tmp] ( size_t i ) { return i * tmp + tmp; };
break;
}
std::vector<float> v( N );
std::iota( v.begin(), v.end(), value );
return v;
}
int main()
{
for ( float x : f( 10, First ) ) std::cout << x << ' ';
std::cout << std::endl;
for ( float x : f( 10, Second ) ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
The output is
0 5 10 15 20 25 30 35 40 45
5 10 15 20 25 30 35 40 45 50
Of course you may use your own lambda expressions that include some mathematical functions like sin
I need a structure to hold a value based on a key that has a range.
My implementation is C++, so any STL or Boost would be excellent.
I have as range-key, which are doubles, and value
[0,2) -> value1
[2,5) -> value2
[5,10) -> value3
etc
Such that a search of 1.23 should return value1, and so on.
Right now I am using a vector containing all three parts, key1/key2/value, with custom searching, but it feels like there should be a cleaner structure.
Edit: Thanks all. Given the ranges in this case are supposed to be contiguous and non-overlapping, the use of upper_bound will work just fine. Thanks for the class Range solutions as well, they are filed away for future reference.
class Range
{
public:
Range( double a, double b ):
a_(a), b_(b){}
bool operator < ( const Range& rhs ) const
{
return a_ < rhs.a_ && b_ < rhs.b_;
}
private:
double a_;
double b_;
};
int main()
{
typedef std::map<Range, double> Ranges;
Ranges r;
r[ Range(0, 2) ] = 1;
r[ Range(2, 5) ] = 2;
r[ Range(5, 10) ] = 3;
Ranges::const_iterator it1 = r.find( Range( 2, 2 ) );
std::cout << it1->second;
Ranges::const_iterator it2 = r.find( Range( 2, 3 ) );
std::cout << it2->second;
Ranges::const_iterator it3 = r.find( Range( 6, 6 ) );
std::cout << it3->second;
return 0;
}
If your ranges are contiguous and non-overlapping, you should use std::map and the upper_bound member function. Or, you could use a sorted vector with the upper_bound algorithm. Either way, you only need to record the lowest value of the range, with the upper part of the range being defined by the next higher value.
Edit: I phrased that confusingly, so I decided to provide an example. In coding the example, I realized you need upper_bound instead of lower_bound. I always get those two confused.
typedef std::map<double, double> MyMap;
MyMap lookup;
lookup.insert(std::make_pair(0.0, dummy_value));
lookup.insert(std::make_pair(2.0, value1));
lookup.insert(std::make_pair(5.0, value2));
lookup.insert(std::make_pair(10.0, value3));
MyMap::iterator p = lookup.upper_bound(1.23);
if (p == lookup.begin() || p == lookup.end())
...; // out of bounds
assert(p->second == value1);
How about something along these lines:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <sstream>
class Range
{
public:
Range(double lower, double upper) : lower_(lower), upper_(upper) {};
Range(const Range& rhs) : lower_(rhs.lower_), upper_(rhs.upper_) {};
explicit Range(const double & point) : lower_(point), upper_(point) {};
Range& operator=(const Range& rhs)
{
lower_ = rhs.lower_;
upper_ = rhs.upper_;
return * this;
}
bool operator < (const Range& rhs) const
{
return upper_ <= rhs.lower_;
}
double lower_, upper_;
};
typedef std::string Thing;
typedef std::map<Range, Thing> Things;
std::string dump(const std::pair<Range,Thing> & p)
{
stringstream ss;
ss << "[" << p.first.lower_ << ", " << p.first.upper_ << ") = '" << p.second << "'" << endl;
return ss.str();
}
int main()
{
Things things;
things.insert( std::make_pair(Range(0.0, 5.0), "First") );
things.insert( std::make_pair(Range(5.0, 10.0), "Second") );
things.insert( std::make_pair(Range(10.0, 15.0), "Third") );
transform( things.begin(), things.end(), ostream_iterator<string> (cout,""), dump );
cout << "--------------------------------------" << endl;
things[Range(1.5)] = "Revised First";
transform( things.begin(), things.end(), ostream_iterator<string> (cout,""), dump );
return 0;
}
... program output:
[0, 5) = 'First'
[5, 10) = 'Second'
[10, 15) = 'Third'
--------------------------------------
[0, 5) = 'Revised First'
[5, 10) = 'Second'
[10, 15) = 'Third'