2

SCENARIO: I have a simple application that checks its RSS feed and looks if there's a newer version available. Thus, I want to check if the current version is less than the one present on the RSS feed. Ideally as simple as:

CURRENTVERSION < updateVersion

PROBLEM: The versioning consists of major.minor.revision.build and I don't know how to parse this into a number to perform the version check.

These are the parameters to compare:

#define CURRENTVERSION = 0,2,5,1

The version downloaded from the web is "0.2.6.1" ( as a string).

What would be the best way to check if one is less than the other?

I've tried converting it to a double, but the value becomes 0.2 (only the first . is being parsed, rest is ignored).

CONSTRAINT: It must not be a solution using .NET libraries as the application must work when no .NET framework is present.

(EDIT) I settled for the following solution, thanks to Karthik T's answer.

struct Version
{
    Version(string versionStr)
    {
        sscanf(versionStr.c_str(), "%d.%d.%d.%d", &major, &minor, &revision, &build);
    }

    bool operator<(const Version &otherVersion)
    {
        if(major < otherVersion.major)
            return true;
        if(minor < otherVersion.minor)
            return true;
        if(revision < otherVersion.revision)
            return true;
        if(build < otherVersion.build)
            return true;
        return false;
    }

    int major, minor, revision, build;
};
hippietrail
  • 15,848
  • 18
  • 99
  • 158
Amadeus Hein
  • 706
  • 1
  • 4
  • 12
  • 1
    First a question: why do you `#define` the version as a equality sign, followed by a comma separated list od integers, compared to a dot separated list of integers in the web version? I don't see any sense there. You could just `#define CURRENTVERSION "0.2.5.1"` or even better `const char CURRENTVERSION[] = "0.2.5.1";` since there's no need to use a macro here. – Arne Mertz Jan 17 '13 at 07:49
  • @AmadeusHein Your comparison method is incorrect. For example, it claims that `Version("1.0.0.0")` is less than `Version("0.2.5.1")`. After each `if(x < otherVersion.x) return true;` there should be a `if(otherVersion.x < x) return false;`. – reima Dec 31 '13 at 00:20
  • 1
    Please don't put your solution into your question; add it as an answer instead. It's OK to add a answer that is a refinement of another answer. – Edward Brey Jul 15 '14 at 11:59
  • http://stackoverflow.com/a/34484221/1318830 – mkungla Dec 29 '15 at 00:54
  • Your solution is flawed. The code you have would try to update from 3.1.2.2 to 2.2.2.2 since minor is less for example. – super Jul 11 '18 at 17:57

3 Answers3

10
struct Version{
    Version(std::string versionStr);     //remember to use  versionStr.c_str() if using C functions like sscanf or strtok
    bool operator<(const Version &other); // could and perhaps should be a free function

    int major,minor,revision,build;
};


bool needtoUpdate = Version(CURRENTVERSION)<Version(latestVersion);

Please fill in the definitions.

Also, your #define is a mistake. You want it like below. Or use a const char * if you can.

#define CURRENTVERSION "0.2.5.1"

You can use sscanf or something like strtok to parse in the constructor.

Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • Thanks! This looks great. I'm very new to C++ (as I'm sure you figured :)), would you mind terribly to show the constructor parse as well? – Amadeus Hein Jan 17 '13 at 07:54
  • Why would he want to `#define` it at all when he can (and thus should) use a const variable instead? – Arne Mertz Jan 17 '13 at 07:55
  • 2
    I would much prefer to leave that as an exercise to you :) Look up "tokenize string C++" or have a look at http://www.cplusplus.com/reference/cstring/strtok/ – Karthik T Jan 17 '13 at 07:56
  • I don't know if it's the best way, but this application is a wrapper around a .NET dependent application, and there's a build script changing CURRENTVERSION (to that of the .NET application). I thought it would be easiest for it to change a #define. – Amadeus Hein Jan 17 '13 at 07:57
  • @ArneMertz restricting my changes to the question mostly, `const char *` is prefered, but in such cases I would prefer define since I would need to define the var in a .cpp to avoid multiple definitions etc. – Karthik T Jan 17 '13 at 07:57
  • @KarthikT I think that's for the best :) Thanks again! – Amadeus Hein Jan 17 '13 at 07:58
  • @KarthikT: constants at namespace scope have internal linkage in C++, so you can define them inside a header and have no ODR violation. So a `const char[]` would do as well as `const char * const` (note that the pointer has to be const to get the internal linkage). If you prefer to make the internal linkage explicit, you can either declare it `static` (C style) or inside an unnamed namespace (C++ style). There is *really* no excuse to use `#define` for constants in C++ – Arne Mertz Jan 17 '13 at 08:33
  • @ArneMertz I was not aware of that rule, In that case yes `const char *` makes more sense. – Karthik T Jan 17 '13 at 08:38
9

I recommend to just use a function whose argument is two strings stand for the version number. There are no need to use a struct or class to do such a simple thing. I think it is better to keep things simple. For example:

#include <stdio.h>
#include <string.h>

/*
 * return 1 if v1 > v2
 * return 0 if v1 = v2
 * return -1 if v1 < v2
 */

int cmpVersion(const char *v1, const char *v2)
{
    int i;
    int oct_v1[4], oct_v2[4];
    sscanf(v1, "%d.%d.%d.%d", &oct_v1[0], &oct_v1[1], &oct_v1[2], &oct_v1[3]);
    sscanf(v2, "%d.%d.%d.%d", &oct_v2[0], &oct_v2[1], &oct_v2[2], &oct_v2[3]);

    for (i = 0; i < 4; i++) {
        if (oct_v1[i] > oct_v2[i])
            return 1;
        else if (oct_v1[i] < oct_v2[i])
            return -1;
    }

    return 0;
}

int main()
{
    printf("%d\n", cmpVersion("0.1.2.3", "0.2.3.4"));
}
Bin Wang
  • 2,697
  • 2
  • 24
  • 34
  • 1
    Using a struct has 0 performance overhead and can even improve the performance in the simple case of sorting a vector of versions as opposed to holding a vector of strings which represent a version. In addition, "There are no need to use a struct or class to do such a simple thing" is just dismissing the entire concept of Types and the great benefits of the type system. A simple example is not being able to pass a string that does not represent a version to a function that expect a version. – ZivS Jun 14 '21 at 09:57
3

If you have a compiler that support C++11, you can std::tuple to make the code cleaner:

 struct Version
  {
      Version(std::string versionStr)
      {
          sscanf(versionStr.c_str(), "%d.%d.%d.%d", &major, &minor, &revision, &build);
      }
      VersionTuple ToTuple() const
      {
          return VersionTuple{ major,minor,revision,build };
      }
      int major, minor, revision, build;
  };

  bool operator<(const Version& v1, const Version& v2)
  {
      return v1.ToTuple() < v2.ToTuple();
  }
ZivS
  • 2,094
  • 2
  • 27
  • 48