Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Faster constant string appending without macro

Tags:

c++

So when combining strings, oftentimes there are constant components, for example:

std::string s;
s += initial_string;
s += "const string";
s += terminating_string;

That's just a demonstration, string operations can be quite a bit more complex and in-depth. So, when doing the const part, the implementation ends up "not knowing" the length and effectively does a strlen() on it. Clearly this is a waste, as the length is known at compile time. I've tested that replacing the const string part with this is quite a bit faster (substantially more in x64 for whatever reason):

s.append("const string",12);

It's annoying, time-consuming, and error-prone to actually count the characters, so this is a little better:

s.append("const string",sizeof("const string")-1);

That's still somewhat error prone (i.e. change the first part but forget to change the second part) so a macro can help this:

#define strnsizeof(s) s,sizeof(s)-1
s.append(strnsizeof("const string"));

Question 1: Anybody have a better/cleaner solution to this?

I've also got an extended string class where I use the << operator for concatenating strings and various other object types. Similar issue here, this is nice and clean (to me):

s << initial_string << "const string" << terminating_string;

When I have an operator for my own object type (of which length is a component) the append operation is fast and easy, but when it gets the const char * here again, I don't get the length even though it's constant at compile time. So I can speed that up by creating a little structure that takes a const char * and length along the lines of:

s << initial_string
    << MyStr::ConstBuf(strnsizeof("const string"))
    << terminating_string;

Boy is that getting ugly. So I could macro that out too, e.g.:

#define MyStrConst(s) MyStr::ConstBuf(s,sizeof(s)-1)
s << initial_string
    << MyStrConst("const string")
    << terminating_string;

Better but not great.

Question 2: Anybody got a better/cleaner solution than encapsulating the constant string?

like image 265
mark Avatar asked Aug 08 '13 15:08

mark


2 Answers

Comments to the question resulted in a template like the following:

template<size_t SZ> std::string& operator<<( std::string &s, const char(&arr)[SZ] ) {
    s.append( arr, SZ-1 );
    return s;
}

So instead of s += "const string" the template is utilized when doing:

s << "const string"

Additionally, I was able to update my extended string class such that the following utilizes a template to get the constant size as well:

s << initial_string << "const string" << terminating_string;

EDIT: this does not work as expected:

typedef struct { char buffer[32]; } ST;
ST st = { "1234" };
s << st.buffer;  // results in s with size 31!

This can be solved via a non-const template, e.g.:

template<size_t SZ> std::string& operator<<( std::string &s, char(&arr)[SZ] ) {
    s.append( arr ); // NOTE not using SZ here so a strlen happens
    return s;
}

So now:

s << st.buffer;  // results in s with size 4

Except:

const ST cst = &st;
s << cst.buffer;  // results in s with size 31 again...

Same issue when buffer is in a class, as you'd expect.

like image 180
mark Avatar answered Oct 17 '22 13:10

mark


Write to your compiler manufacturer and ask them why they don't optimise for this case. Then, hopefully, they'll add constant string concatenation to the list of optimisations and everyone's code will go faster without having to do anything!

That would be my favourite solution.

like image 2
gbjbaanb Avatar answered Oct 17 '22 13:10

gbjbaanb