Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String literal reference class [duplicate]

Tags:

c++

c++14

In C++1y, can I have a reference class that binds to a string literal but not to char* or char[]& or similar?

class LitRf{
  const char* data_;
  size_t size_;
public:
  LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ }
};
like image 781
PSkocik Avatar asked Mar 03 '16 12:03

PSkocik


3 Answers

I guess the best you can do is to use const char (&s)[N] (with template<size_t N>) as the parameter type. But it also binds to any const char array other than a string literal.

Add a deleted non-const char array constructor to prohibit calling it with a non-const array.

class LitRf
{
    const char* data_;
    Sz size_;
public:
    template<size_t N>
    LitRf(char const (&s)[N])
        : data_{s}, size_{N}
    {}

    template<size_t N>
    LitRf(char (&s)[N]) = delete;
};

Other than that, you can use a macro wrapper, which (when the constructor is never used without it) makes it only possible to construct an object from a literal, not even via a variable.

#define MakeLitRf(s) LitRf(s "")

The idea is to concatenate two string literals, of which the second one is just an empty string. This is only possible if the first is also a string literal; putting a variable there is a syntax error. After macro expansion, the compiler sees something like LitRf("foo" "") which is equivalent to LitRf("foo"). Some examples:

auto x = MakeLitRf("foo");  // works

const char *foo = "foo";
auto x = MakeLitRf(foo);    // fails

auto x = LitRf(foo);        // works, but we want it to fail...

In the last case, the user unintentionally (or intentionally?) didn't use the macro, making our work worthless. To make it fail too, add a hidden parameter to the constructor, which is required to be added when called directly (and in the macro's definition, of course):

class LitRf
{
    const char* data_;
    Sz size_;
public:
    // Called by the macro MakeLitRf. Unlikely to be called directly unless the user knows what he's doing.
    LitRf(const char *s, void *)
        : data_{s}, size_{N}
    {}

    // Called without macro! Throw a compiler error, explaining what's wrong.
    LitRf(const char *s)
    {
        static_assert(false, "Please use the macro `MakeLitRf` with a string literal to construct a `LitRf`.");
    }
};

#define MakeLitRf(s) LitRf(s "", nullptr)
like image 137
leemes Avatar answered Oct 05 '22 09:10

leemes


C++11 removed the only formal way to detect a string literal as such, namely via its implicit conversion to pointer to non-const.

Anyway using the little trick one had to employ a macro.

In C++11 and later the best you can do is to establish a strong convention that an array of const character type is a literal.

I.e., with your example,

class LitRf
{
private:
    const char* data_;
    Sz size_;

    template< size_t n >
    LitRf( char (&)[n] ) = delete;

public:
    template< size_t n >
    LitRf( char const (&s)[n] )
        : data_{s}, size_{sizeof(s) - 1}
    {}
};

Note the use of size_t instead of a possibly signed type Sz. This ensure that the code will compile with g++. Unfortunately that compiler, or older versions of it, has a bug where it's quite insistent on size_t, or else it refuses to accept the code.

like image 41
Cheers and hth. - Alf Avatar answered Oct 05 '22 11:10

Cheers and hth. - Alf


class LitRf{
  const char* data_;
  Sz size_;
public:
  LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ }
  LitRf(char*) = delete;
};
like image 41
user764486 Avatar answered Oct 05 '22 11:10

user764486