Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restrict passed parameter to a string literal

I have a class to wrap string literals and calculate the size at compile time.

The constructor looks like this:

template< std::size_t N >
Literal( const char (&literal)[N] );

// used like this
Literal greet( "Hello World!" );
printf( "%s, length: %d", greet.c_str(), greet.size() );

There is problem with the code however. The following code compiles and I would like to make it an error.

char broke[] = { 'a', 'b', 'c' };
Literal l( broke );

Is there a way to restrict the constructor so that it only accepts c string literals? Compile time detection is preferred, but runtime is acceptable if there is no better way.

like image 522
deft_code Avatar asked Sep 30 '11 16:09

deft_code


4 Answers

There is a way to force a string literal argument: make a user defined literal operator. You can make the operator constexpr to get the size at compile time:

constexpr Literal operator "" _suffix(char const* str, size_t len) {
    return Literal(chars, len);
}

I don't know of any compiler that implements this feature at this time.

like image 105
R. Martinho Fernandes Avatar answered Oct 16 '22 04:10

R. Martinho Fernandes


Yes. You can generate compile time error with following preprocessor:

#define IS_STRING_LITERAL(X) "" X ""

If you try to pass anything other than a string literal, the compilation will fail. Usage:

Literal greet(IS_STRING_LITERAL("Hello World!"));  // ok
Literal greet(IS_STRING_LITERAL(broke)); // error
like image 34
iammilind Avatar answered Oct 16 '22 03:10

iammilind


With a C++11 compiler with full support for constexpr we can use a constexpr constructor using a constexpr function, which compiles to a non-const expression body in case the trailing zero character precondition is not fulfilled, causing the compilation to fail with an error. The following code expands the code of UncleBens and is inspired by an article of Andrzej's C++ blog:

#include <cstdlib>

class Literal
{
  public:

    template <std::size_t N> constexpr
    Literal(const char (&str)[N])
    : mStr(str),
      mLength(checkForTrailingZeroAndGetLength(str[N - 1], N))
    {
    }

    template <std::size_t N> Literal(char (&str)[N]) = delete;

  private:
    const char* mStr;
    std::size_t mLength;

    struct Not_a_CString_Exception{};

    constexpr static
    std::size_t checkForTrailingZeroAndGetLength(char ch, std::size_t sz)
    {
      return (ch) ? throw Not_a_CString_Exception() : (sz - 1);
    }
};

constexpr char broke[] = { 'a', 'b', 'c' };

//constexpr Literal lit = (broke); // causes compile time error
constexpr Literal bla = "bla"; // constructed at compile time

I tested this code with gcc 4.8.2. Compilation with MS Visual C++ 2013 CTP failed, as it still does not fully support constexpr (constexpr member functions still not supported).

Probably I should mention, that my first (and preferred) approach was to simply insert

static_assert(str[N - 1] == '\0', "Not a C string.")

in the constructor body. It failed with a compilation error and it seems, that constexpr constructors must have an empty body. I don't know, if this is a C++11 restriction and if it might be relaxed by future standards.

like image 25
k.st. Avatar answered Oct 16 '22 02:10

k.st.


No there is no way to do this. String literals have a particular type and all method overload resolution is done on that type, not that it's a string literal. Any method which accepts a string literal will end up accepting any value which has the same type.

If your function absolutely depends on an item being a string literal to function then you probably need to revisit the function. It's depending on data it can't guarantee.

like image 27
JaredPar Avatar answered Oct 16 '22 02:10

JaredPar