marton78
marton78

Reputation: 3989

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?

Upvotes: 4

Views: 2310

Answers (3)

Jonathan Wakely
Jonathan Wakely

Reputation: 171303

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

Upvotes: 4

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.

Upvotes: 5

To elaborate further on your template version:

#include <iostream>

template <typename T>
inline void pushstring(T str);

template <int N>
inline void pushstring(const char (&str) [N])
{
   std::cout << N << std::endl;
}

template <>
inline void pushstring(const char *str)
{
   std::cout << str << std::endl;
}

See a test run here:
Wrong parameter -> linker error: http://ideone.com/vZbj6
Rigt parameter -> runs great :) : http://ideone.com/iJBAo

Upvotes: 2

Related Questions