Printing like Perl in C++ - c++

Can I do something like the following Perl code in C++ ?
i.e Replicate $string n times ?
printf ("%s\n",$string x 4);

While C++ doesn't have an operator x, C++ is a flexible enough language that you can write a named operator x whose job it is to multiply strings:
#include <string>
namespace strop {
struct x_op {} x;
struct x_lhs { std::string s; };
x_lhs operator*( std::string s, x_op ) {
return {std::move(s)};
}
std::string operator*( x_lhs&& lhs, std::size_t rhs ) {
std::string retval;
for( std::size_t i = 0; i < rhs; ++i ) {
retval += lhs.s;
}
return retval;
}
}
use:
#include <iostream>
using strop::x;
int main () {
std::string foo = "foo";
std::cout << (foo *x* 5) << "\n"; // if you prefer std::cout
printf("%s", (foo *x* 5).c_str()); // if you prefer printf
}
The above uses some trivial C++11 features, but not essentially.
C++03 version (it is less efficient):
#include <string>
namespace strop {
struct x_op {} x;
struct x_lhs { std::string s; x_lhs(std::string s_):s(s_) {} };
x_lhs operator*( std::string s, x_op ) {
return x_lhs(s);
}
std::string operator*( x_lhs lhs, std::size_t rhs ) {
std::string retval;
for( std::size_t i = 0; i < rhs; ++i ) {
retval += lhs.s;
}
return retval;
}
}
amusingly, this works with string literals:
std::cout << "bar" *x* 3 << "\n";
because they are turned into std::strings implicitly. If you are using printf, you still need to wrap it in ().c_str(), or you are risking undefined behavior (even if you never mention std::string).
You could instead overload some other non-named operator, but overloading operators when you own neither type in the overload is pretty rude, and can lead to complications.
The above technique uses the placeholder types of x_op and x_lhs to ensure that the overloaded operator will only come into effect when we are interacting with our named operator.
The choice of * to wrap our named operator x is because the operation is multiplication-like.
Bonus: Insane version of operator*, which uses bit values of the rhs to reduce the amount of string wrangling:
std::string operator*( x_lhs lhs, std::size_t rhs ) {
std::string retval;
auto done = [rhs]( std::size_t n ) {
return !(i < (sizeof(rhs)*8) && !(rhs &~std::size_t((1<<i)-1)));
};
for( int i = 0; true; ++i) {
if (!done(i+1)) {
if (rhs & (1 << i))
retval += lhs;
lhs += lhs;
continue;
} else {
// high bit must be set, or we'd be done before, unless rhs was 0:
if (rhs != 0)
retval += std::move(lhs);
break;
}
}
return retval;
}
which has not been tested, but amuses me.

Preface: overloading operators is a nuanced practice. If you're not careful, it can lead to code which is difficult to read and maintain. That said ...
You could try overloading operator*(), but in order to make that work you'll need to use string objects and not bare c-strings. The reason is that C++ does not allow you overload operators which only reference built-in types. The std::string class is not built in to the language.
For example, the following function will do what you intend with std::string objects. Note that the return type is a const char* to be compatible with printf().
const char* operator*(const std::string& input, size_t repeat)
{
std::string output;
for (size_t ii=0; ii<repeat; ++ii)
output += input;
return output.c_str();
}
With that function you can then use the syntax you prefer if the strings are stored in std::string objects:
int main(int argc, char** argv)
{
std::string input = argv[1];
printf("%s\n", input*3); // works
printf("%s\n", std::string(argv[1])*3); // works
//printf("%s\n", argv[1]*3); // compile error
}
A more natural C++ way of doing it would be to use <iostream>, which does not require you to mix char* and std::string types in the overloaded operator.
std::string operator*(const std::string& input, size_t repeat)
{
std::string output;
for (size_t ii=0; ii<repeat; ++ii)
output += input;
return output;
}
int main(int argc, char** argv)
{
std::cout << std::string(argv[1])*3 << std::endl;
}

No, not directly. You need to do it yourself:
for (size_t i = 0; i < 4; ++i)
printf ("%s", astring);
printf ("\n");

As impressive as #Yakk's overloading is, why not just define a simple function?
std::string repeat(const std::string& s, int times) {
std::string res;
res.reserve(s.size() * times);
for (int i = 0; i < times; ++i)
res += s;
return s;
}
Then you can do
std::cout << repeat("foo", 4) << std::endl;
Prints foofoofoofoo.

You can just use a for loop:
for(int i=0;i<n;i++)
cout<<string;

Related

Unify the express of a complex number in initializer list and istream field

As an old c99 person, I was often stubled upon the curly brakets initialization. In the `initializer_list`, I have to use {r, i} for a complex number. On the other hand, I have to use (r, i) for `complex` in the istream field. Here, I cut a part of my class that is able to run and give examples under codeblock 20.03 with MinGW 8.1.0.
#ifndef __tMatrix_class__
#define __tMatrix_class__
#include <iostream>
#include <initializer_list>
#include <iomanip>
#include <complex>
#include <sstream>
template <typename T> class tMatrix
{
public:
T *ptr;
int col, row, size;
inline T* begin() const {return ptr;}
inline T* end() const {return this->ptr + this->size;}
inline T operator()(const int i, const int j) const { return ptr[i*col+j]; } // r-value
inline T&operator()(const int i, const int j) { return ptr[i*col+j]; } //l-value
inline tMatrix(): col{0}, row{0}, size{0}, ptr{0} {;}
tMatrix(const int i, const int j): col(j), row(i), size(i*j) {
ptr = new T [this->size] ; }
tMatrix(const std::initializer_list< std::initializer_list<T> > s):tMatrix<T>(s.size(), s.begin()->size())
{
int j = 0;
for (const auto& i : s) { std::copy (i.begin(), i.end(), ptr + j*col); ++j ; }
}
tMatrix(const tMatrix<T>&a) : tMatrix<T>(a.row, a.col)
{
std::copy(a.begin(), a.end(), this->ptr);
}
tMatrix<T>& operator=(tMatrix<T>&&a)
{
this->col = a.col;
this->row = a.row;
delete [] this->ptr;
this->ptr = a.ptr;
a.ptr = nullptr;
return *this;
}
tMatrix<T>& operator=(const tMatrix<T>&a)
{
if (col==a.cpl && row==a.row) std::copy(a.begin(), a.end(), this->ptr);
else { tMatrix<T>&&v(a); *this = std::move(v);}
return *this;
}
tMatrix<T>& operator=(const std::initializer_list<std::initializer_list<T> > a)
{
tMatrix<T> &&v = a;
*this = std::move(v);
return *this;
}
~tMatrix() {delete [] this->ptr;}
void operator<<(const char*s)
{
std::stringstream ss;
ss.str(s);
for (int i=0; i<this->size; i++){
if (ss.good()) ss >> this->ptr[i];
else return;
}
}
}; //end of class tMatrix
template <typename X> std::ostream& operator<<(std::ostream&p, const tMatrix<X>&a)
{
p << std::fixed;
for (int i=0; i<a.row; i++) {
for (int j=0; j <a.col; j++) p << std::setw(12) << a(i, j);
p << std::endl;
}
return p;
}
using CMPLX = std::complex<double>;
using iMatrix = tMatrix<int>;
using rMatrix = tMatrix<double>;
using cMatrix = tMatrix< CMPLX >;
#endif
int main()
{
cMatrix cx(2,2);
cx = { { {1,2},{3,4} }, { {5,6}, {7,8} } };
std::cout << cx << std::endl;
cx << "(1,2) (3,4)";
std::cout << cx << std::endl;
return 0;
}
The above code renders correct format of complex number, and prints
$ ./ttt_mtx_init_fin_tmp.exe
(1.000000,2.000000)(3.000000,4.000000)
(5.000000,6.000000)(7.000000,8.000000)
(1.000000,2.000000)(3.000000,4.000000)
(5.000000,6.000000)(7.000000,8.000000)
But if I use the `()` in the initializer_list and `{}` in the istream filed, the results are all wrong. If I chagned the relavant part of main() to :
cx = { { (1,2),(3,4) }, { (5,6), (7,8) } };
std::cout << cx << std::endl;
cx << "{1,2} {3,4}";
std::cout << cx << std::endl;
Which renders all wrong values (compared with above):
$ ./ttt_mtx_init_fin_tmp.exe
(2.000000,0.000000)(4.000000,0.000000)
(6.000000,0.000000)(8.000000,0.000000)
(2.000000,0.000000)(4.000000,0.000000)
(6.000000,0.000000)(8.000000,0.000000)
I found it is rather confusion. So, my questions: is there a way to make these two expressions a same form? Many thanks for any helps.
I do not know any way to make std::istream::operator>> use { and } for std::complex, but if you are fine with using a helper, then you can replace the () in the input with {} and forward the input to the original operator>>:
#include <iostream>
#include <complex>
#include <sstream>
#include <algorithm>
template <typename T>
struct complex_reader {
std::complex<T>& target;
};
template <typename T>
complex_reader<typename T::value_type> get_complex_reader(T& t){ return {t};}
template <typename T>
std::istream& operator>>(std::istream& in,complex_reader<T> cr){
std::string input;
std::getline(in,input,'}'); // read till `}`
std::replace(input.begin(),input.end(),'{','(');
input += ')';
std::stringstream ss{input};
ss >> cr.target; // call the original >>
return in;
}
int main()
{
std::stringstream ss{"{2,2}"};
std::complex<double> x;
ss >> get_complex_reader(x);
std::cout << x;
}
Output:
(2,2)
However, you would have to write a similar helper to get consistent output (you may not provide an operator<< for std::complex<T> directly). Also note that the above implementation is a little simplistic. It reads from the stream until it encounters a }. For invalid input this may result in undesired effects and more sophisticated input validation is required.
Note that the operator>> takes the complex_helper by value to allow passing temporaries. Thats fine, because the member is a (non-const) reference.
This is not an answer, but a reasoning of my choice. After a series of cross conversions with `largest_prime_is_463035818`, I figured out what is my best choice for now (many thanks to his time and patience). A bottom line is becoming clear to me that I will not alter the input format of istream that is too much changed for pratical purpose, since file input is the major method to fetch data for a large matrix.
Under this constrain, I try to make the appearance of initializer_list as friendly as possible. I did some experiments, and found that the complex_literals expression is acceptable by initializer_list. And it looks ok to me.
using namespace std::complex_literals;
int main()
{
cMatrix cx(3,2);
cx = { { 1+2.2j , 4j}, { 5.3+6.5j , 8.3j}, {8.3, 5.6+4j} };
std::cout << cx << std::endl;
cx << " (1,2) (3,4) (5,6) (7,8) (2.3, 3.4) (2,7.8) ";
std::cout << cx << std::endl;
return 0;
}
And it works.
$ ./a.exe
(1.000000,2.200000) (0.000000,4.000000)
(5.300000,6.500000) (0.000000,8.300000)
(8.300000,0.000000) (5.600000,4.000000)
(1.000000,2.000000) (3.000000,4.000000)
(5.000000,6.000000) (7.000000,8.000000)
(2.300000,3.400000) (2.000000,7.800000)
Thank you for your patience, and please let me know if there are better ways.

Use of std::vector results in unknown output C++

I'm unable to figure out why the output I received isn't just "00110" but has other giberrish characters in it. Not sure what's wrong with my vector push_back.. It definitely makes sense to me. If I changed it to std::string implementation, it would give a correct output. But in this case, I would need to use vector for proper encapsulation of the object's state. I've been debugging for a few hours now, but still can't find out why. Hope anyone is able to help! Thanks! Note: main() can't be modified.
#include <iostream>
#include <vector>
template<size_t NumBits>
class bitsetts
{
private:
static const unsigned int NO_OF_BITS = CHAR_BIT * sizeof(int); //32 bits
static const unsigned NumBytes = (NumBits - 7) /8;
unsigned char array[NumBytes];
public:
bitsetts() { }
void set(size_t bit, bool val = true) {
if (val == true)
{
array[bit] |= (val << bit );
}
else
{
array[bit] &= (val << bit );
}
}
bool test(size_t bit) const {
return array[bit] & (1U << bit );
}
const std::string to_string()
{
std::vector<char> str;
for (unsigned int i=NumBits; i-- > 0;)
str.push_back('0' + test(i));
return str.data();
}
friend std::ostream& operator<<(std::ostream& os, const bitsetts& ob)
{
for (unsigned i = NumBits; i-- > 0;)
os << ob.test(i);
return os << '\n';
}
};
int main()
{
try
{
bitsetts<5> bitsetts;
bitsetts.set(1);
bitsetts.set(2);
const std::string st = bitsetts.to_string();
if (st != "00110")
{
std::cout << st << std::endl;
throw std::runtime_error{ "-" };
}
}
catch (const std::exception& exception)
{
std::cout << "Conversion failed\n";
}
}
You are filling the std::vector with char values and then constructing a std::string from the raw char data using the std::string constructor that takes a single const char* parameter. That constructor expects the char data to be null-terminated, but you are not pushing a null terminator into your vector, which is why you get extra garbage on the end of your std::string.
So, either push a null terminator into the vector, eg:
const std::string to_string()
{
std::vector<char> str;
for (unsigned int i=NumBits; i-- > 0;)
str.push_back('0' + test(i));
str.push_back('\0'); // <-- add this!
return str.data();
}
Or, use a different std::string constructor that can take the vector's size() as a parameter, eg:
const std::string to_string()
{
std::vector<char> str;
for (unsigned int i=NumBits; i-- > 0;)
str.push_back('0' + test(i));
return std::string(str.data(), str.size()); // <-- add size()!
}
On a side note: your to_string() method should be marked as const, eg:
const std::string to_string() const
Which would then allow you to use to_string() inside of your operator<<, eg:
friend std::ostream& operator<<(std::ostream& os, const bitsetts& b)
{
return os << b.to_string() << '\n';
}

Macro Overloading with Different Signatures

Is it possible to overload macros that can perform different operators (=, +=, -=, ++, --, etc) with the same macro name?
I would like to achieve something like this:
int main() {
LOG_STAT("hello") << "world";
LOG_STAT("hello") = 5;
LOG_STAT("hello") += 10;
}
I tried the following and the issue I am having is that I can not redeclare the macro LOG_STAT as it has already been defined. Sample code below, hopefully you get the idea.
#define LOG_STAT(x) Stat(x).streamAdd()
#define LOG_STAT(x) Stat(x).add() // redeclare error here
class Stat {
public:
Stat(const char *type_ ) : type(type_) {}
~Stat(){ std::cout << type << " " << stream.str().c_str() << " " << number << std::endl;}
int& add() { return number; }
std::ostringstream& streamAdd() { return stream; }
const char * type;
int number;
std::ostringstream stream;
};
Create operators for your class:
Stat& Stat::operator += (int rhs)
{
number += rhs;
return *this;
}
Stat operator + (const Stat& lhs, int rhs)
{
Stat res(lhs);
res += rhs;
return res;
}
template <typename T>
Stat& operator << (Stat& stat, const T&value)
{
stat.stream << value;
return stat;
}
Then you may directly use
Stat("hello") << "world";
Stat("hello") = 5;
Stat("hello") += 10;
(You may still use your MACRO with #define LOG_STAT Stat)

Is there a built in function for std::string in C++ to compare two strings alphabetically when either string can be uppercase or lowercase?

I know for C++ that basic comparative operators can accomplish the task if both words are either entirely lower or entirely upper case. I have an array of strings and letters can vary from lower to upper. Here is a small examle of the kind of strings I can be working with:
"ABUNDANT LIFE CH"
"NEW LIFE WMN MNSTRY"
"NEW LIFE ASSEMBLY"
I know in Java there exists the function String.compareToIgnoreCase(). Is there a C++ equivalent of this function?
I don't know of any case-insensitive functions in the standard library, but you can specify a custom predicate for std::equal:
std::string a("hello");
std::string b("HELLO");
std::cout << std::equal(a.begin(), a.end(), b.begin(),
[] (const char& a, const char& b)
{
return (std::tolower(a) == std::tolower(b));
});
For a solution which takes locale into account, refer to Case insensitive std::string.find().
#include <locale>
template<typename charT = std::string::value_type>
struct my_equal {
my_equal( const std::locale& loc ) : loc_(loc) {}
bool operator()(charT ch1, charT ch2) {
return std::toupper(ch1, loc_) == std::toupper(ch2, loc_);
}
private:
const std::locale& loc_;
};
int main()
{
std::string a("hello");
std::string b("HELLO");
std::cout << std::equal(a.begin(), a.end(), b.begin(),
my_equal<>(std::locale()));
}
Yes there is a case insensitive way to compare strings in C++. The key is that std::string is a template:
template <class charT,
class traits = char_traits<charT>,
class Allocator = allocator<charT>>
class basic_string;
The traits here control how the charT's relate to each other. For normal std::string, they do what you'd expect, but we can just write our own traits that are case insensitive:
struct case_insensitive_traits
: char_traits<char>
{
static bool eq(char a, char b) { return tolower(a) == tolower(b); }
static bool ne(char a, char b) { return !eq(a, b); }
static bool lt(char a, char b) { return tolower(a) < tolower(b); }
static bool gt(char a, char b) { return tolower(a) > tolower(b); }
static int compare(const char* a, const char* b, size_t n)
{
for (size_t i = 0; i < n; ++i) {
int delta = tolower(a[i]) - tolower(b[i]);
if (delta != 0) return delta;
}
return 0;
}
static const char* find(const char* s, size_t n, char c)
{
c = tolower(c);
for (size_t i = 0; i < n; ++i, ++s) {
if (tolower(*s) == c) return s;
}
return nullptr;
}
};
With that:
using case_insensitive_string = std::basic_string<char, case_insensitive_traits>;
case_insensitive_string a{"hello"};
case_insensitive_string b{"hElLo"};
assert(a == b);
You can use Boost String Algorithms:
#include <string>
#include <cassert>
#include <boost/algorithm/string.hpp>
int main() {
std::string s { "Test" };
assert(boost::iequals(s, "TEST"));
}
In C++ usually less-than (bool less(type, type)) is used in places of tri-value function compare (int cmp(type, type)). Of course each one of them can be trivially defined in terms of the other.
Here's something that can easily be plugged into STL algorithms:
template<class String>
struct ciless {
locale l_;
explicit ciless(locale l = locale()) : l_(l) {}
bool operator() (
String const &a
, String const &b) const
{
auto fa = a.begin();
auto fb = b.begin();
while (fa != a.end()
&& fb != b.end()
&& (tolower(*fa, l_) == tolower(*fb, l_)))
{
++fa;
++fb;
}
return
(fa == a.end() && fb != b.end())
|| (
fa != a.end()
&& fb != b.end()
&& tolower(*fa, l_) < tolower(*fb, l_));
}
};
And here's something that can convert less() into java-style compare():
template<class T, class Less = std::less<T>>
struct compare
{
Less l_;
explicit compare(Less l = Less()) : l_(l) {}
int operator() (
T const &a
, T const &b) const
{
if (l_(a, b))
return -1;
if (l_(b, a))
return 1;
return 0;
}
};
Nothing standard, but if you happen to be developing for Windows or have access to a Posix interface you could use the following:
https://msdn.microsoft.com/en-us/library/k59z8dwe.aspx
// didn't run it through a compiler
// but it would look like something like this:
{
using namespace std;
string a = "HELLO"s;
string b = "HelLO"s;
bool bIsMatch = _stricmp(a.c_str(), b.c_str()) == 0; // bIsMatch = true
}
There's a much simpler solution that doesn't involve coding loops (ugh!) or Boost:
string a = "Foo";
string b = "foo"
return !_stricmp(a.str(), b.str());
The strings are converted to char[]s, then compared, case-insensitive. Returns true if they're equal.
(My biased opinion: C++ should provide this functionality as a string method. Forcing millions of users to hand-code loops for such a common operation is disgusting, and introduces unnecessary "noise" into the code.)

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;
}