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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With