Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient passing of string literals in C++

While writing code for wrapping Lua, I came across the need for passing string literals and started to wonder which way it is the most efficient.

I have the choice between two functions:

  1. void lua_pushstring (lua_State* L, const char* str);
  2. void lua_pushlstring(lua_State* L, const char* str, size_t len);

Of course, the 1st function uses strlen() internally, the 2nd one is thus faster.

Now, if it's known at compile time, I'd like to avoid calculating string length, as laid out here and here:

// Overload 1
template <size_t N>
inline void pushstring(lua_State* L, const char (&str) [N])
{
    lua_pushlstring(L, str, N-1);
}

As nice as this function works when called with a string literal: pushstring(L, "test"); of course it doesn't compile when called with a const char*, for example in a longer function which is in a .cpp file:

// this is in a .cpp file
void long_function(lua_State* L, const char* str)
{
    // do lots of stuff
    pushstring(L, str);  // compile error
    // do more stuff
}

Now if I add

// Overload 2
inline void pushstring(lua_State* L, const char* str)
{
    lua_pushstring(L, str);
}

it is for some reason (C++ overload resolution is tricky) preferred over Overload 1, which is thus never called.

Is there a clever way to fix that?

like image 643
marton78 Avatar asked Oct 16 '25 01:10

marton78


2 Answers

I would offer two options:

void f( const char*, int );
template <int N> void f( const char (&str)[N] ) {
   f( str, N-1 );
}

(or rather std::size_t), Now users that have a string literal can call the second that will internally dispatch to the first. Users that don't have a literal but a const char* are responsible for providing the correct size.

like image 91
David Rodríguez - dribeas Avatar answered Oct 18 '25 13:10

David Rodríguez - dribeas


If you declare both with the second one forwarding to the first:

void lua_pushlstring(lua_State* L, const char* str, size_t len);

inline void lua_pushstring (lua_State* L, const char* str)
{ lua_pushlstring(L, str, strlen(str)); }

Then a decent compiler will optimise away the strlen call when you call the second function with a literal, e.g. it will inline

lua_pushstring(L, "hello");

and because strlen on a literal can be optimised to a constant, it will replace it with a call to:

lua_pushlstring(L, "hello", 5);

That gives you the simple syntax of calling the two-argument form, without paying for strlen on literals.

When the length is already known it can be passed:

lua_pushlstring(L, s.c_str(), s.length());

Or this works too, but with an unnecessary call to strlen

lua_pushstring(L, s.c_str());
like image 27
Jonathan Wakely Avatar answered Oct 18 '25 13:10

Jonathan Wakely



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!