STL map custom comparator - c++

I am trying to use a user defined type as a map key with a custom comparator as follows.
#include <map>
#include <iostream>
class RangeKey {
public:
int start;
int end;
RangeKey(int start, int end) : start(start), end(end) {
}
bool withinRange(int value, bool inclusive) const {
if (inclusive) {
return (value >= start && value <= end);
} else {
return (value > start && value < end);
}
}
bool overlapsWith(const RangeKey& r) const {
if (r.withinRange(start, true) ||
r.withinRange(end, true) ||
(start < r.start && end > r.end)) {
return true;
}
return false;
}
};
class RangeKeyComparator {
public:
bool operator()(const RangeKey& a, const RangeKey& b) const {
if (a.overlapsWith(b)) {
return true;
} else {
return a.start < b.start;
}
}
};
int main() {
std::map<RangeKey, int, RangeKeyComparator> m;
m.insert(std::pair<RangeKey, int>(RangeKey(1, 2), 1));
auto it = m.find(RangeKey(1, 2));
std::cout << it->first.start << "\n";
std::cout << it->first.end << "\n";
std::cout << it->second << "\n";
return 0;
}
The idea is to consider two RangeKey instances as equal if their ranges overlap. However when I try to retrieve a value after the insertion it gives me some garbage values as the main function output. What am I doing wrong here?

The comparator for a map needs to be a "strict weak ordering," i.e. it cannot be that Comp(A,B) returns true and also Comp(B,A) returns true. Your comparator is a violation of this.

Related

std::map<struct,struct>::find is not finding a match, but if i loop thru begin() to end() i see the match right there

struct chainout {
LONG cl;
std::string cs;
bool operator<(const chainout&o)const {
return cl < o.cl || cs < o.cs;
}
} ;
struct chainin{
std::string tm;
std::string tdi;
short mss;
LONG pinid;
bool operator<(const chainin&o)const {
return mss < o.mss || pinid < o.pinid || tm<o.tm; //no tdi right now it's always empty
}
};
std::map <chainin,chainout> chainmap;
std::map<chainin,chainout>::iterator it;
chainin ci;
chainout co;
string FADEDevicePinInfo::getNetAtPinIdTmTidMss (const LONG p,const string tm, const string tid,const LONG mss){
ci.tm=tm;
// ci.tdi=tid;
ci.tdi="";
ci.mss=(short)mss;
ci.pinid=p;
for (it=chainmap.begin();it!=chainmap.end();it++){
if(it->first.pinid==ci.pinid && it->first.tm==ci.tm&&it->first.mss==ci.mss && it->first.tdi==ci.tdi){
cout << "BDEBUG: found p["; cout<<it->first.pinid; cout<<"] tm["; cout<<it->first.tm.c_str();cout<<"] mss[";cout<<it->first.mss;cout<<"] : ";cout<<it->second.chainSignal.c_str();cout<<endl;
}
}
it=chainmap.find(ci);
if(it == chainmap.end()){
MSG(SEV_T,("no pin data found for pin[%ld]/tm[%s]/tdi[%s]/mss[%ld]",ci.pinid,ci.tm.c_str(),ci.tdi.c_str(),ci.mss));
}
return it->second.cs;
}
This is both printing the successfully found line, and then throwing the sev_t error due to map::find not returning a match. what did i do wrong?
I added print statements thruout the < function, but it seems to be ordering the map correctly, and when i do the lookup, it seems to find the correct mss/pinid, but then only sees one tm, which is the wrong tm.
As noted in comments, you have a bad comparison operator. If you don't know what order the objects should be sorted in, then neither does std::map or any other sorted container.
When you have multiple things to compare, consider deciding which is most important, and use std::tie to compare them, as demonstrated here:
#include <string>
#include <iostream>
struct chainout {
int cl;
std::string cs;
bool operator<(const chainout&o)const {
return std::tie(cl, cs) < std::tie(o.cl, o.cs);
}
};
int main(){
chainout a{ 1, "b" };
chainout b{ 2, "a" };
std::cout << (a < b) << std::endl;
std::cout << (b < a) << std::endl;
}
The operator< for both of your structs are implemented incorrectly.
std::map requires key comparisons to use Strict Weak Ordering. That means when your structs want to compare multiple fields, they need to compare later fields only when earlier fields compare equal. But you are not checking for that condition. You are returning true if any field in one instance compares less-than the corresponding field in the other instance, regardless of the equality (or lack of) in the other fields. So you are breaking SWO, which causes undefined behavior in std::map's lookups.
Try this instead:
struct chainout {
LONG cl;
std::string cs;
bool operator<(const chainout &o) const {
/*
if (cl < o.cl) return true;
if (cl == o.cl) return (cs < o.cs);
return false;
*/
return (cl < o.cl) || ((cl == o.cl) && (cs < o.cs));
}
};
struct chainin{
std::string tm;
std::string tdi;
short mss;
LONG pinid;
bool operator<(const chainin &o) const {
if (mss < o.mss) return true;
if (mss == o.mss) {
if (pinid < o.pinid) return true;
if (pinid == o.pinid) return (tm < o.tm);
}
return false;
}
};
An easier way to implement this is to use std::tie() instead, which has its own operator< to handle this for you, eg:
struct chainout {
LONG cl;
std::string cs;
bool operator<(const chainout &o) const {
return std::tie(cl, cs) < std::tie(o.cl, o.cs);
}
};
struct chainin{
std::string tm;
std::string tdi;
short mss;
LONG pinid;
bool operator<(const chainin &o) const {
return std::tie(mss, pinid, tm) < std::tie(o.mss, o.pinid, o.tm);
}
};
Either way, then std::map::find() should work as expected, eg:
std::map<chainin, chainout> chainmap;
string FADEDevicePinInfo::getNetAtPinIdTmTidMss (const LONG p, const string tm, const string tid, const LONG mss)
{
chainin ci;
ci.tm = tm;
//ci.tdi = tid;
ci.tdi = "";
ci.mss = (short) mss;
ci.pinid = p;
std::map<chainin, chainout>::iterator it = chainmap.find(ci);
if (it != chainmap.end()) {
cout << "BDEBUG: found"
<< " p[" << it->first.pinid << "]"
<< " tm[" << it->first.tm << "]"
<< " mss[" << it->first.mss << "]"
<< " : " << it->second.cs
<< endl;
}
}

Abstraction for bitset element iteration

I have a custom bitset class implementation in C++. I often iterate over the indexes of bits that are set in the bitset (i.e. for bitset '10011' I want to iterate over numbers 0, 3, 4.) This iteration can be implemented as follows:
struct Bitset {
uint64_t* data_;
size_t chunks_;
std::vector<int> Elements() const {
std::vector<int> ret;
for (size_t i=0;i<chunks_;i++){
uint64_t td = data_[i];
while (td) {
ret.push_back(i*BITS + __builtin_ctzll(td));
td &= ~-td;
}
}
return ret;
}
};
void Iterate(Bitset bitset) {
for (int b : bitset.Elements()) {
std::cout << "bit: " << b << std::endl;
}
}
The above implementation provides clean code for the iteration, but it involves an unnecessary heap allocation with the vector. The following version which essentially inlines the Elements() function is often faster:
void Iterate(Bitset bitset) {
int chunks = bitset.chunks_;
for (int i = 0; i < chunks; i++) {
uint64_t td = bitset.data_[i];
while (td) {
std::cout << "bit: " << i*BITS + __builtin_ctzll(td) << std::endl;
td &= ~-td;
}
}
}
What would be a good way to implement an abstraction for the iteration so that it would be as clean as the above version, but also with no performance cost.
Just iterate over your class. Provide your own implementation of an iterator class for your Bitset and provide begin() and end() methods. A simplest (untested!) implementation could look something like this:
#include <vector>
#include <cstdint>
#include <iostream>
struct Bitset {
uint64_t* data_;
size_t chunks_;
struct iterator {
uint64_t *pnt;
uint_fast8_t pos;
iterator(uint64_t *pnt, size_t pos) :
pnt(pnt), pos(pos) {}
bool operator !=(const iterator& o) {
return o.pnt != pnt || o.pos != pos;
}
void operator ++() {
pos++;
if (pos == 64) {
pnt++;
pos = 0;
}
}
bool operator *() {
return *pnt & (1 << pos);
}
};
iterator begin() { return iterator(data_, 0); }
iterator end() { return iterator(data_ + chunks_, 64); }
};
void Iterate(Bitset bitset) {
for (auto&& b : bitset) {
std::cout << "bit: " << b << std::endl;
}
}
I believe for your strange while (td) { ... i*BITS + __builtin_ctzll(td) ... loop that I don't understand that could be something along (untested!):
constexpr int BITS = 100000;
struct Bitset {
uint64_t* data_;
size_t chunks_;
struct iterator {
uint64_t *data_;
int i = 0;
uint64_t td = 0;
iterator(uint64_t *data_, int i, uint64_t td) :
data_(data_), i(i), td(td) {}
bool operator !=(const iterator& o) {
return o.data_ != data_ || o.i != i || o.td != td;
}
void operator ++() {
if (td == 0) {
td = *data_;
data_++;
} else {
td &= ~-td;
}
}
bool operator *() {
return i * BITS + __builtin_ctzll(td);
}
};
iterator begin() { return iterator(data_, 0, *data_); }
iterator end() { return iterator(data_ + chunks_, 0, 0); }
};
As KamilCuk suggested, I used an iterator to solve this problem. Now the implementation looks like:
struct Bitset {
uint64_t* data_;
size_t chunks_;
class BitsetIterator {
private:
const Bitset* const bitset_;
size_t pos_;
uint64_t tb_;
public:
BitsetIterator(const Bitset* const bitset, size_t pos, uint64_t tb) :
bitset_(bitset), pos_(pos), tb_(tb) { }
bool operator!=(const BitsetIterator& other) const {
return pos_ != other.pos_ || tb_ != other.tb_;
}
const BitsetIterator& operator++() {
tb_ &= ~-tb_;
while (tb_ == 0 && pos_ < bitset_->chunks_) {
pos_++;
if (pos_ < bitset_->chunks_) {
tb_ = bitset_->data_[pos_];
}
}
return *this;
}
int operator*() const {
return pos_*BITS + __builtin_ctzll(tb_);
}
};
BitsetIterator begin() const {
size_t pos = 0;
while (pos < chunks_ && data_[pos] == 0) {
pos++;
}
if (pos < chunks_) {
return BitsetIterator(this, pos, data_[pos]);
} else {
return BitsetIterator(this, pos, 0);
}
}
BitsetIterator end() const {
return BitsetIterator(this, chunks_, 0);
}
};
void Iterate(Bitset bitset) {
for (int b : bitset) {
std::cout << "bit: " << b << std::endl;
}
}
This avoids heap allocation and is much faster than the version that uses vector. I'm not sure if this provides exactly same performance as the version without any abstractions, but it should be very close.

Why is trying to store a pointer to function ambiguous

Here is my code:
#include <functional>
#include <iostream>
#include<vector>
using namespace std;
// vector iterator
template <class T> class vit
{
private:
//vector<T>::iterator it;
vector<T> m_v;
function<bool (T, T)> m_fptr;
int len, pos;
public:
vit(vector<T> &v) { this->m_v = v; len = v.size(); pos = 0;};
// it= v.begin(); };
bool next(T &i) {
//if(it == m_v.end()) return false;
if(pos==len) return false;
//i = *it;
i = m_v[pos];
//if(idle) { idle = false ; return true; }
//it++;
pos++;
return true;};
//bool idle = true;
void set_same(function<bool (T,T)> fptr) { m_fptr = fptr ;};
//void set_same(function<bool(int, int)> fun) { return ; }
bool grp_begin() {
return pos == 0 || ! m_fptr(m_v[pos], m_v[pos-1]); };
bool grp_end() {
return pos == len || ! m_fptr(m_v[pos], m_v[pos+1]); };
};
bool is_same(int a, int b) { return a == b; }
main()
{
vector<int> v ={ 1, 1, 2, 2, 2, 3, 1, 1, 1 };
int total;
for(auto it = v.begin(); it != v.end(); it++) {
if(it == v.begin() || *it != *(it-1)) {
total = 0;
}
total += *it;
if(it+1 == v.end() || *it != *(it+1)) {
cout << total << endl;
}
}
cout << "let's gry a group" <<endl;
vit<int> g(v);
int i;
while(g.next(i)) { cout << i << endl; }
cout << "now let's get really fancy" << endl;
vit<int> a_vit(v);
//auto is_same = [](int a, int b) { return a == b; };
a_vit.set_same(is_same);
//int total;
while(a_vit.next(i)) {
if(a_vit.grp_begin()) total = 0;
total += i;
if(a_vit.grp_end()) cout << total << endl ;
}
}
When I compile it with g++ -std=c++11 iter.cc -o iter, I get the result:
iter.cc: In function 'int main()':
iter.cc:63:17: error: reference to 'is_same' is ambiguous
a_vit.set_same(is_same);
^
iter.cc:37:6: note: candidates are: bool is_same(int, int)
bool is_same(int a, int b) { return a == b; }
^
In file included from /usr/include/c++/5.3.0/bits/move.h:57:0,
from /usr/include/c++/5.3.0/bits/stl_pair.h:59,
from /usr/include/c++/5.3.0/utility:70,
from /usr/include/c++/5.3.0/tuple:38,
from /usr/include/c++/5.3.0/functional:55,
from iter.cc:1:
/usr/include/c++/5.3.0/type_traits:958:12: note: template<class, class> struct std::is_same
struct is_same;
^
By way of explanation, I have created a class called 'vit'. It does two things: iterate over a vector, and determine if a new group has been reached.
The class function 'set_same' is supposed to store a function provided by the calling class to determine if two adjacent elements of a vector are in the same group. However, I can't seem to store the function in the class for future use by grp_begin() and grp_end() on account of the ostensible ambiguity of is_same.
What gives?
There is an is_same function defined by you and there is a struct is_same defined by the C++ Standard Library. Since you are using namespace std, your compiler doesn't know which is_same you meant to use.
It's what the error says: it's not clear whether you mean your is_same (in the global namespace) or the class template is_same (in namespace std).
You may disambiguate as follows:
::is_same
… with the leading :: meaning "in the global namespace".
Though you should consider putting your code in a namespace of its own.
Thanks guys. This is my first time touching C++ after more than a decade. I have cleaned up the code, and used a lambda to bring the "is_same" function closer to where it is called.
Did you spot the bug in my code? 'pos' was off-by-one when calling grp_begin() and grp_end(). Here is the revised code:
#include <functional>
#include <iostream>
#include <vector>
// vector iterator
template <class T> class vit
{
private:
std::vector<T> m_v;
std::function<bool (T, T)> m_fptr;
int len, pos;
public:
vit(std::vector<T> &v) { m_v = v; len = v.size(); pos = -1;};
bool next(T &val) {
pos++;
if(pos==len) return false;
val = m_v[pos];
return true;};
void set_same(std::function<bool (T,T)> fptr) { m_fptr = fptr ;};
bool grp_begin() {
return pos == 0 || ! m_fptr(m_v[pos], m_v[pos-1]); };
bool grp_end() {
return pos+1 == len || ! m_fptr(m_v[pos], m_v[pos+1]); };
};
main()
{
std::vector<int> v ={ 1, 1, 2, 2, 2, 3, 1, 1, 1 };
vit<int> a_vit(v);
std::function<bool (int, int)> is_same = [](int a, int b) { return a == b; };
a_vit.set_same(is_same);
int i, total;
while(a_vit.next(i)) {
if(a_vit.grp_begin()) total = 0;
total += i;
if(a_vit.grp_end()) std::cout << total << std::endl ;
}
}
My class definition isn't bullet-proof and could be better: if the user forgets to 'set-same', for example, they'll be referring a random memory address as a function.
Nevertheless, I'm pretty chuffed with my solution so far. The class caller is relieved of all the bookkeeping relating iterating over the vector, and working out if a group boundary has been crossed.
The calling code looks very compact and intuitive to me.I can see C++ being my go to language.

How to recursively compare vectors?

I'm trying to write a function which recursively checks if a given vector A is in any contiguous block in vector B. For example, if A={5,6,7} and B={1,2,3,4,5,6,7}, it should return true. If B = {1,2,3,4,5,7,6}, it should return false. Currently, my code keeps outputting true because I don't think my logic is correct. I have not been able to modify it to produce any results yet. Any help will be appreciated!
bool r_app(vector<int>& a1, vector<int> b1, size_t k)
{
k=0;
if (a1.size() == 0) {
cout << "true";
return true;;
}
for (int i=0;i<a1.size();i++){
for(int j=0;j<b1.size();j++){
if (a1.at(i)==b1.at(j)) {
cout << "true" << endl;
return true;
}
}
cout << "false" << endl;
return false;
return r_app(a1,b1,k+1);
}
}
EDIT: So this is what I got from Smac89, and I added the cout lines so that when I call the function in main, it outputs either true or false. The function currently outputs true for every true input, but doesnt output false.. I'm not sure why.
bool r_app(std::vector<int>& a1, std::vector<int> &b1, std::size_t start)
{
std::size_t savedPos = start + 1, k = 0;
for (; k < a1.size() && start < b1.size() && a1[k] == b1[start];
k++, start++)
{
if (k != 0 && a1[0] == b1[start])
savedPos = start;
}
if (k == a1.size())
cout << "true" << endl;
return true;
if (start < b1.size())
return r_app(a1, b1, savedPos);
cout << "false" << endl;
return false;
}
template <typename T>
bool r_app(std::vector<T>& a1, std::vector<T> &b1, std::size_t start) {
std::size_t savedPos = start + 1, k = 0;
for (; k < a1.size() && start < b1.size() && a1[k] == b1[start];
k++, start++)
{
if (k != 0 && a1[0] == b1[start])
savedPos = start;
}
if (k == a1.size())
return true;
if (start < b1.size())
return r_app(a1, b1, savedPos);
return false;
}
template <typename T>
bool r_app(std::vector<T>& a1, std::vector<T>& b1) {
return r_app(a1, b1, 0);
}
Example:
http://rextester.com/COR69755
EDIT:
V2
Now more efficient searching - start a search either where the last search ended or at a character that matches the start of the search string
You can also print out where the first match occurred by looking at savedPos - 1
You need1 two recursive functions here if you want to do everything recursively.
One to test if the sequence is found at a particular point, and one to use this other function to test for equality at every point. Here is a simple template implementation that will work with any STL container that allows iteration, as well as non-STL sequences (such as raw arrays):
template <typename NeedleIterator, typename HaystackIterator = NeedleIterator>
bool recursive_sequence_equals(
NeedleIterator needle_begin,
NeedleIterator needle_end,
HaystackIterator haystack_begin,
HaystackIterator haystack_end)
{
// The sequences are equal because we reached the end of the needle.
if (needle_begin == needle_end) {
return true;
}
// If we reached the end of the haystack, or the current elements are not equal
// then the sequences are not equal here.
if (haystack_begin == haystack_end || *needle_begin != *haystack_begin) {
return false;
}
// We are not at the end of the haystack nor the needle, and the elements were
// equal. Move on to the next element.
return recursive_sequence_equals(
++needle_begin, needle_end,
++haystack_begin, haystack_end);
}
template <typename NeedleIterator, typename HaystackIterator = NeedleIterator>
HaystackIterator recursive_sequence_find(
NeedleIterator needle_begin,
NeedleIterator needle_end,
HaystackIterator haystack_begin,
HaystackIterator haystack_end)
{
// We reached the end with no match.
if (haystack_begin == haystack_end) {
return haystack_begin;
}
// If the sequences are equal at this point, return the haystack iterator.
if (recursive_sequence_equals(needle_begin, needle_end,
haystack_begin, haystack_end)) {
return haystack_begin;
}
// Check the next position in the haystack.
return recursive_sequence_find(
needle_begin, needle_end,
++haystack_begin, haystack_end);
}
Used like this:
std::vector<int> a = { 5, 6, 7 };
std::vector<int> b = { 1, 2, 3, 4, 5, 6, 7 };
auto found = recursive_sequence_find(
a.begin(), a.end(),
b.begin(), b.end());
if (found != b.end()) {
// There was a match, found is an iterator to the beginning of the match in b.
} else {
// No match. (Or both containers were empty!)
}
(Demo)
1 Technically you can do this with one function if you use some extra parameters to convey whether or not you are in the middle of an equality test. However this adds a lot of extra complication to the logic for no gain. It's easier and more straightforward to implement using two different recursive functions.
#include <stdio.h>
#include <string.h>
#include <vector>
#include <iostream>
using namespace std;
bool r_app(vector<int> a, vector<int> b, int starta, int startb)
{
if(a.size() == 0) return 0;
if(starta == 0) {
int i=0;
while(1) {
while(a[0] != b[i] && i < b.size())
i++;
if(i >= b.size()) return 0;
if(r_app(a, b, starta+1, i+1) == 1)
return 1;
else i++;
}
}
else {
if(starta == a.size())
return 1;
else if(startb == b.size())
return 0;
else if(a[starta] == b[startb])
return r_app(a, b, starta+1, startb+1);
else
return 0;
}
}
int main() {
vector<int> a;
vector<int> b;
b.push_back(1);
b.push_back(2);
b.push_back(3);
b.push_back(4);
b.push_back(5);
a.push_back(3);
a.push_back(4);
cout << r_app(a,b,0,0);
return 0;
}
It will be easier if you do not use recursion. Also, KMP algorithm will give you much optimized solution.
My try, using iterator:
#include <iostream>
#include <vector>
#include <algorithm>
template<typename Iter>
bool r_app(Iter first_a, Iter last_a, Iter first_b, Iter last_b)
{
if(first_a == last_a) //trivial case
return true;
auto found = std::find(first_b, last_b, *first_a);
if(found == last_b)
return false;
else
return r_app(++first_a, last_a, found, last_b); //recursion
}
int main()
{
std::vector<int> a{5,6,7};
std::vector<int> b{1,2,3,4,5,6,7};
std::cout << r_app(a.begin(),a.end(),b.begin(),b.end());
return 0;
}

Compare versions as strings

Comparing version numbers as strings is not so easy...
"1.0.0.9" > "1.0.0.10", but it's not correct.
The obvious way to do it properly is to parse these strings, convert to numbers and compare as numbers.
Is there another way to do it more "elegantly"? For example, boost::string_algo...
I don't see what could be more elegant than just parsing -- but please make use of standard library facilities already in place. Assuming you don't need error checking:
void Parse(int result[4], const std::string& input)
{
std::istringstream parser(input);
parser >> result[0];
for(int idx = 1; idx < 4; idx++)
{
parser.get(); //Skip period
parser >> result[idx];
}
}
bool LessThanVersion(const std::string& a,const std::string& b)
{
int parsedA[4], parsedB[4];
Parse(parsedA, a);
Parse(parsedB, b);
return std::lexicographical_compare(parsedA, parsedA + 4, parsedB, parsedB + 4);
}
Anything more complicated is going to be harder to maintain and isn't worth your time.
I would create a version class.
Then it is simple to define the comparison operator for the version class.
#include <iostream>
#include <sstream>
#include <vector>
#include <iterator>
class Version
{
// An internal utility structure just used to make the std::copy in the constructor easy to write.
struct VersionDigit
{
int value;
operator int() const {return value;}
};
friend std::istream& operator>>(std::istream& str, Version::VersionDigit& digit);
public:
Version(std::string const& versionStr)
{
// To Make processing easier in VersionDigit prepend a '.'
std::stringstream versionStream(std::string(".") + versionStr);
// Copy all parts of the version number into the version Info vector.
std::copy( std::istream_iterator<VersionDigit>(versionStream),
std::istream_iterator<VersionDigit>(),
std::back_inserter(versionInfo)
);
}
// Test if two version numbers are the same.
bool operator<(Version const& rhs) const
{
return std::lexicographical_compare(versionInfo.begin(), versionInfo.end(), rhs.versionInfo.begin(), rhs.versionInfo.end());
}
private:
std::vector<int> versionInfo;
};
// Read a single digit from the version.
std::istream& operator>>(std::istream& str, Version::VersionDigit& digit)
{
str.get();
str >> digit.value;
return str;
}
int main()
{
Version v1("10.0.0.9");
Version v2("10.0.0.10");
if (v1 < v2)
{
std::cout << "Version 1 Smaller\n";
}
else
{
std::cout << "Fail\n";
}
}
First the test code:
int main()
{
std::cout << ! ( Version("1.2") > Version("1.3") );
std::cout << ( Version("1.2") < Version("1.2.3") );
std::cout << ( Version("1.2") >= Version("1") );
std::cout << ! ( Version("1") <= Version("0.9") );
std::cout << ! ( Version("1.2.3") == Version("1.2.4") );
std::cout << ( Version("1.2.3") == Version("1.2.3") );
}
// output is 111111
Implementation:
#include <string>
#include <iostream>
// Method to compare two version strings
// v1 < v2 -> -1
// v1 == v2 -> 0
// v1 > v2 -> +1
int version_compare(std::string v1, std::string v2)
{
size_t i=0, j=0;
while( i < v1.length() || j < v2.length() )
{
int acc1=0, acc2=0;
while (i < v1.length() && v1[i] != '.') { acc1 = acc1 * 10 + (v1[i] - '0'); i++; }
while (j < v2.length() && v2[j] != '.') { acc2 = acc2 * 10 + (v2[j] - '0'); j++; }
if (acc1 < acc2) return -1;
if (acc1 > acc2) return +1;
++i;
++j;
}
return 0;
}
struct Version
{
std::string version_string;
Version( std::string v ) : version_string(v)
{ }
};
bool operator < (Version u, Version v) { return version_compare(u.version_string, v.version_string) == -1; }
bool operator > (Version u, Version v) { return version_compare(u.version_string, v.version_string) == +1; }
bool operator <= (Version u, Version v) { return version_compare(u.version_string, v.version_string) != +1; }
bool operator >= (Version u, Version v) { return version_compare(u.version_string, v.version_string) != -1; }
bool operator == (Version u, Version v) { return version_compare(u.version_string, v.version_string) == 0; }
https://coliru.stacked-crooked.com/a/7c74ad2cc4dca888
Here's a clean, compact C++20 solution, using the new spaceship operator <=>, and Boost's string split algorithm.
This constructs and holds a version string as a vector of numbers - useful for further processing, or can be disposed of as a temporary. This also handles version strings of different lengths, and accepts multiple separators.
The spaceship operator lets us provide results for <, > and == operators in a single function definition (although the equality has to be separately defined).
#include <compare>
#include <boost/algorithm/string.hpp>
struct version {
std::vector<size_t> data;
version() {};
version(std::string_view from_string) {
/// Construct from a string
std::vector<std::string> data_str;
boost::split(data_str, from_string, boost::is_any_of("._-"), boost::token_compress_on);
for(auto const &it : data_str) {
data.emplace_back(std::stol(it));
}
};
std::strong_ordering operator<=>(version const& rhs) const noexcept {
/// Three-way comparison operator
size_t const fields = std::min(data.size(), rhs.data.size());
// first compare all common fields
for(size_t i = 0; i != fields; ++i) {
if(data[i] == rhs.data[i]) continue;
else if(data[i] < rhs.data[i]) return std::strong_ordering::less;
else return std::strong_ordering::greater;
}
// if we're here, all common fields are equal - check for extra fields
if(data.size() == rhs.data.size()) return std::strong_ordering::equal; // no extra fields, so both versions equal
else if(data.size() > rhs.data.size()) return std::strong_ordering::greater; // lhs has more fields - we assume it to be greater
else return std::strong_ordering::less; // rhs has more fields - we assume it to be greater
}
bool operator==(version const& rhs) const noexcept {
return std::is_eq(*this <=> rhs);
}
};
Example usage:
std::cout << (version{"1.2.3.4"} < version{"1.2.3.5"}) << std::endl; // true
std::cout << (version{"1.2.3.4"} > version{"1.2.3.5"}) << std::endl; // false
std::cout << (version{"1.2.3.4"} == version{"1.2.3.5"}) << std::endl; // false
std::cout << (version{"1.2.3.4"} > version{"1.2.3"}) << std::endl; // true
std::cout << (version{"1.2.3.4"} < version{"1.2.3.4.5"}) << std::endl; // true
int VersionParser(char* version1, char* version2) {
int a1,b1, ret;
int a = strlen(version1);
int b = strlen(version2);
if (b>a) a=b;
for (int i=0;i<a;i++) {
a1 += version1[i];
b1 += version2[i];
}
if (b1>a1) ret = 1 ; // second version is fresher
else if (b1==a1) ret=-1; // versions is equal
else ret = 0; // first version is fresher
return ret;
}