Search code examples
c++qtc++11metaprogrammingstatic-assert

try to do a static assertion on a string if possible or fallback to a runtime check when it's not


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

Solution

  • 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()));
    }