8

I want to check if a given string is a valid UUID using boost.

This is what I have come up with by looking at the documentation on the boost website:

void validate_uuid(const std::string& value)
{
    try
    {
        boost::uuids::string_generator stringGenerator;
        (void)stringGenerator(value);
    }

    catch (const std::exception& ex)
    {
        // ...
    }
}

However, this does not always work.

If I call the function with a string that is too short for a valid UUID, an exception is thrown as expected. But if I call the function with an invalid UUID (e.g. 00000000-0000-0000-0000-00000000000K) no exception is thrown.

Please can someone clarify why this is happening.

Also, I've seen the use of boost::lexical_cast to read a string as a UUID as posted here. I'm wondering if I should follow that approach. Any advice appreciated.

Community
  • 1
  • 1
ksl
  • 4,519
  • 11
  • 65
  • 106
  • Have you tried a regular expression? – Mark Ransom Feb 23 '15 at 17:08
  • No. I use boost already and saw the UUID library so I thought I'd try using it. – ksl Feb 23 '15 at 17:21
  • I mention it because of [Boost.Regex](http://www.boost.org/doc/libs/1_57_0/libs/regex/doc/html/index.html) which I believe was the basis of [C++11's `regex`](http://en.cppreference.com/w/cpp/regex) – Mark Ransom Feb 23 '15 at 17:29

3 Answers3

6

The code you had does nothing in terms of validation. Instead it generates a UUID based on the constant passed (like a hash function).

Looking closer I was mistaken. The missing bit of validation appears to be a check on version:

Live On Coliru

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/string_generator.hpp>
#include <iostream>

bool is_valid_uuid(std::string const& maybe_uuid, boost::uuids::uuid& result) {
    using namespace boost::uuids;

    try {
        result = string_generator()(maybe_uuid); 
        return result.version() != uuid::version_unknown;
    } catch(...) {
        return false;
    }
}

int main() {
    std::string maybe_uuid;
    std::cout << "Enter a UUID: ";

    while (std::cin >> maybe_uuid)
    {
        boost::uuids::uuid result;
        bool is_valid = is_valid_uuid(maybe_uuid, result);
        std::cout << "\n'" << maybe_uuid << "' valid: " << std::boolalpha << is_valid << "\n";

        if (is_valid)
            std::cout << "Parsed value: " << result << "\n";
    }
}

Sample output from Coliru: echo 00000000-0000-{0,4}000-0000-000000000000 $(uuidgen) "{$(uuidgen)}" | ./a.out:

Enter a UUID: 
'00000000-0000-0000-0000-000000000000' valid: false

'00000000-0000-4000-0000-000000000000' valid: true
Parsed value: 00000000-0000-4000-0000-000000000000

'a2c59f5c-6c9b-4800-afb8-282fc5e743cc' valid: true
Parsed value: a2c59f5c-6c9b-4800-afb8-282fc5e743cc

'{82a31d37-6fe4-4b80-b608-c63ec5ecd578}' valid: true
Parsed value: 82a31d37-6fe4-4b80-b608-c63ec5ecd578
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for the response @sehe. How does it generate a UUID if the string passed is not a valid representation of a UUID, as in my example? – ksl Feb 24 '15 at 08:00
  • @ksl Seems I was thinking of [`name_generator`](http://www.boost.org/doc/libs/1_57_0/libs/uuid/uuid.html#Name%20Generator). (It's that "generator" part in the name that put me on the wrong foot). Then, yes, you can also use `string_generator`. To validate the UUID, [check the `version()` after parsing](http://www.boost.org/doc/libs/1_57_0/libs/uuid/uuid.html#boost/uuid/uuid.hpp): if it is `version_unknown` (or any other type you don't want to allow), you can make it fail. – sehe Feb 24 '15 at 08:18
  • @ksl I've updated the answer with a fixed demo program that correctly identifies correct/invalid UUIDs – sehe Feb 24 '15 at 10:53
  • 1
    @sehe The second example ( '00000000-0000-4000-0000-000000000000') should not be a valid UUID because considering the general format xxxxxxxx-xxxx-Vxxx-**y**xxx-xxxxxxxxxxxx y - has to be either 8, 9, a, or b. [source](http://en.wikipedia.org/wiki/Universally_unique_identifier#Definition) – Scis Feb 24 '15 at 13:18
  • 1
    @Scis Learning everyday here... In that case, the regex would appear to match your purpose well. I was thinking that violated the responsibility separation, so it would be nicer if Boost Uuid acquired the domain logic. Also, **don't forget** about case insensitivity – sehe Feb 24 '15 at 13:28
5

This seems way easier:

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <iostream>
#include <sstream>

int main()
{
    std::string t1("01234567-89ab-cdef-0123-456789abcdef");
    std::string t2("Test UUID");

    boost::uuids::uuid u;
    std::istringstream iss(t1);
    iss >> u;
    if (iss.good())
        std::cerr << "'" << t1 << "' is a good UUID\n";
    else
        std::cerr << "'" << t1 << "' is not a good UUID\n";

    iss.str(t2);
    iss >> u;
    if (iss.good())
        std::cerr << "'" << t2 << "' is a good UUID\n";
    else
        std::cerr << "'" << t2 << "' is not a good UUID\n";

    return 0;
}

$ g++ -I/usr/local/include -std=c++11 test1.cpp
$ a.out
'01234567-89ab-cdef-0123-456789abcdef' is a good UUID
'Test UUID' is not a good UUID
Andrew
  • 609
  • 7
  • 14
4

Since you already use boost you can use regex to check whether your string is a valid UUID

E.g for UUID version 4 you could use the following code

bool validate_uuid(const std::string& s)
{
   static const boost::regex e("[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}");
   return regex_match(s, e); // note: case sensitive now
}

(As mentioned in this answer and in the wiki there should be a valid version digit and another "special" digit).

Live on coliru.

Community
  • 1
  • 1
Scis
  • 2,934
  • 3
  • 23
  • 37
  • 1
    boost::regex is now also std::regex as of C++11 – Sundae Feb 23 '15 at 20:07
  • @Scis I didn't say C++11 wasn't an option. Not sure why you think that. It is an option. – ksl Feb 24 '15 at 08:05
  • @ksl Sorry my bad, for some reason I thought that the "No" was regarding c11 (someone asked it then deleted the comment). But it doesn't change much, just the fact that you can `#include ` and use `std::regex` instead. Like in [this](http://coliru.stacked-crooked.com/a/c385ee5be489781b) example. – Scis Feb 24 '15 at 09:47