2

I have a class named databaseManager that can open protected and shared database. You can know that a database is protected if it's name starts with a "#". I also have two methods:

  • openProtectedDatabase(QString name) (private method)
  • openSharedDatabase(QString name) (public method)

since 99.99% of the time the user is going to use openSharedDatabase like that:

openSharedDatabase("I_m_a_database")

I want in this specific case to check at compile time that he has the right to do it (understand no "#" at the beginning of the string). so I can throw an error immediately.

Here is what I started to do:

bool DatabaseManager::isDatabaseProtected(QString name) {
    return name[0] == '#';
}

CollaoDatabase &DatabaseManager::openSharedDatabase(QString name){

//if a static assertion is possible
//static_assert(static_assertion_possible(name) && isDatabaseProtected(name), "this database name is protected")

//run time check 
if (isDatabaseProtected(name)) {
    qWarning() << name + " is a protected database";
    return nullptr;
}

return openProtectedDatabase(name);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
Nyashes
  • 652
  • 4
  • 22
  • If you can turn call to something like `openSharedDatabase(StaticString("I_m_a_database"));` with `StaticString` a macro to turn literal string into `std::interger_sequence` then you may check at compile time content of argument... – Jarod42 Nov 24 '15 at 16:32

2 Answers2

0

This isn't possible because static_assert requires a constant at compile time expression that returns an integral value. There's no way for the compile to know what's in your QString, so there's no way to statically assert that it is sane.

It can be done, but you'd need to use a customized string class that accepts a literal at compile-time, doesn't allow mutations, and uses constexpr for all member functions. Given this wrapper implemented as class myString, the code to do the static_assert would look like this:

constexpr unsigned beginsWithHash(myString str)
{
    return str.size() > 0 && str[0] == "#";
}
static_assert(beginsWithHash(dbname), "DB Name must begin with #");

Here's more details.

Edit: On behalf of @jarod42's comment, I should add that you can't pass a parameter from the enclosing function to beginsWithHash unless the enclosing function is also a constexpr. It needs to be given a direct string literal. You'd need to do some funky preprocessor magic and/or some functor tricks to get it to behave how you want and still look/feel clean.

Nick Cano
  • 437
  • 2
  • 7
  • 1
    Unfortunately, arguments are not `constexpr`, so with provided code, OP can just do `static_assert(beginsWithHash("I_m_a_database"), "DB Name must begin with #");`. – Jarod42 Nov 24 '15 at 16:27
0

Ok thanks to you advices, I finally got something that do (kind of) what I wanted to do. I used the answer of this question: Conveniently Declaring Compile-Time Strings in C++ to create a compile-time character sequence and now have two overloads:

  • template <typename ct_str> inline CollaoDatabase &openSharedDatabase()
  • inline CollaoDatabase openSharedDatabase(QString name)

the first is used like this (this one do a static assert):

openSharedDatabase<CT_STR("#I_m_a_protected_name")>();

and the second like this (this one do a runtime check instead):

openSharedDatabase("Hithere");

Here is the code:

#define MACRO_GET_1(str, i) \
    (sizeof(str) > (i) ? str[(i)] : 0)

#define MACRO_GET_4(str, i) \
    MACRO_GET_1(str, i+0),  \
    MACRO_GET_1(str, i+1),  \
    MACRO_GET_1(str, i+2),  \
    MACRO_GET_1(str, i+3)

#define MACRO_GET_16(str, i) \
    MACRO_GET_4(str, i+0),   \
    MACRO_GET_4(str, i+4),   \
    MACRO_GET_4(str, i+8),   \
    MACRO_GET_4(str, i+12)

#define MACRO_GET_64(str, i) \
    MACRO_GET_16(str, i+0),  \
    MACRO_GET_16(str, i+16), \
    MACRO_GET_16(str, i+32), \
    MACRO_GET_16(str, i+48)

#define CT_STR(str) ct_string<MACRO_GET_64(str, 0), 0>

template <char firstL, char... letters>
struct ct_string{
    static char const * c_str() {
        static constexpr char string[]={firstL, letters...,'\0'};
        return string;
    }
    static constexpr char first() {
        return firstL;
    }
};

inline bool isDatabaseProtected(QString name){
    return name[0] == '#';
}
template<typename ct_str> static constexpr inline bool isDatabaseProtected() {
    return ct_str::first() == '#';
}
inline CollaoDatabase &openSharedDatabase(QString name){
    if (isDatabaseProtected(name)) {
        qWarning() << name + " is a protected database";
    }

    return openProtectedDatabase(name);
}

template <typename ct_str> inline CollaoDatabase &openSharedDatabase() {
    static_assert(!isDatabaseProtected<ct_str>(), "you are trying to open a protected database");
    return openProtectedDatabase(QString(ct_str::c_str()));
}
Community
  • 1
  • 1
Nyashes
  • 652
  • 4
  • 22