Fancy padding of wide strings

Clash Royale CLAN TAG#URR8PPP
up vote
3
down vote
favorite
I have a function that adds to a std::wstring some filler characters. The user gives a single filler character. They can also optionally given an initial filler sequence (std::wstring) and an optional final filler sequence (std::wstring) that start and end the padding. These will only be added however if they fit inside the padded length specified by the user.
static std::wstring fillWString(
const std::wstring &stringToFill,
size_t fillLength,
const wchar_t fillChar = L' ',
const std::wstring &initialFill = L"",
const std::wstring &endFill = L"")
const size_t originalStringLength = stringToFill.size();
if (fillLength <= originalStringLength)
return stringToFill;
std::wstring result(stringToFill);
result.resize(fillLength, fillChar);
if (originalStringLength + initialFill.size() <= fillLength)
result.replace(originalStringLength, initialFill.size(), initialFill, 0u);
if (originalStringLength + initialFill.size() + endFill.size() <= fillLength)
result.replace(fillLength - endFill.size(), endFill.size(), endFill, 0u);
return result;
Here is some output:
Analyzed folder: .... PathToSomeFolder
Time step: .......... 0.007045000
Out Flow: ........... 1.2460679423999952
In Flow: ............ -1.2461052960000008
where each line was generated using
fillWString(L"string", 22, '.', L" ", L" ") << value;
Specifically, I was wondering what sort of tips you guys might give to improve this. Also, I was just wondering if copy-elision is guaranteed to occur here if the string is indeed padded?
c++ strings formatting
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment |Â
up vote
3
down vote
favorite
I have a function that adds to a std::wstring some filler characters. The user gives a single filler character. They can also optionally given an initial filler sequence (std::wstring) and an optional final filler sequence (std::wstring) that start and end the padding. These will only be added however if they fit inside the padded length specified by the user.
static std::wstring fillWString(
const std::wstring &stringToFill,
size_t fillLength,
const wchar_t fillChar = L' ',
const std::wstring &initialFill = L"",
const std::wstring &endFill = L"")
const size_t originalStringLength = stringToFill.size();
if (fillLength <= originalStringLength)
return stringToFill;
std::wstring result(stringToFill);
result.resize(fillLength, fillChar);
if (originalStringLength + initialFill.size() <= fillLength)
result.replace(originalStringLength, initialFill.size(), initialFill, 0u);
if (originalStringLength + initialFill.size() + endFill.size() <= fillLength)
result.replace(fillLength - endFill.size(), endFill.size(), endFill, 0u);
return result;
Here is some output:
Analyzed folder: .... PathToSomeFolder
Time step: .......... 0.007045000
Out Flow: ........... 1.2460679423999952
In Flow: ............ -1.2461052960000008
where each line was generated using
fillWString(L"string", 22, '.', L" ", L" ") << value;
Specifically, I was wondering what sort of tips you guys might give to improve this. Also, I was just wondering if copy-elision is guaranteed to occur here if the string is indeed padded?
c++ strings formatting
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Named-value return elision isn't required by the standard, but it would be a poor implementation that didn't bother, at least when optimizing.
â Toby Speight
1 hour ago
add a comment |Â
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I have a function that adds to a std::wstring some filler characters. The user gives a single filler character. They can also optionally given an initial filler sequence (std::wstring) and an optional final filler sequence (std::wstring) that start and end the padding. These will only be added however if they fit inside the padded length specified by the user.
static std::wstring fillWString(
const std::wstring &stringToFill,
size_t fillLength,
const wchar_t fillChar = L' ',
const std::wstring &initialFill = L"",
const std::wstring &endFill = L"")
const size_t originalStringLength = stringToFill.size();
if (fillLength <= originalStringLength)
return stringToFill;
std::wstring result(stringToFill);
result.resize(fillLength, fillChar);
if (originalStringLength + initialFill.size() <= fillLength)
result.replace(originalStringLength, initialFill.size(), initialFill, 0u);
if (originalStringLength + initialFill.size() + endFill.size() <= fillLength)
result.replace(fillLength - endFill.size(), endFill.size(), endFill, 0u);
return result;
Here is some output:
Analyzed folder: .... PathToSomeFolder
Time step: .......... 0.007045000
Out Flow: ........... 1.2460679423999952
In Flow: ............ -1.2461052960000008
where each line was generated using
fillWString(L"string", 22, '.', L" ", L" ") << value;
Specifically, I was wondering what sort of tips you guys might give to improve this. Also, I was just wondering if copy-elision is guaranteed to occur here if the string is indeed padded?
c++ strings formatting
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
I have a function that adds to a std::wstring some filler characters. The user gives a single filler character. They can also optionally given an initial filler sequence (std::wstring) and an optional final filler sequence (std::wstring) that start and end the padding. These will only be added however if they fit inside the padded length specified by the user.
static std::wstring fillWString(
const std::wstring &stringToFill,
size_t fillLength,
const wchar_t fillChar = L' ',
const std::wstring &initialFill = L"",
const std::wstring &endFill = L"")
const size_t originalStringLength = stringToFill.size();
if (fillLength <= originalStringLength)
return stringToFill;
std::wstring result(stringToFill);
result.resize(fillLength, fillChar);
if (originalStringLength + initialFill.size() <= fillLength)
result.replace(originalStringLength, initialFill.size(), initialFill, 0u);
if (originalStringLength + initialFill.size() + endFill.size() <= fillLength)
result.replace(fillLength - endFill.size(), endFill.size(), endFill, 0u);
return result;
Here is some output:
Analyzed folder: .... PathToSomeFolder
Time step: .......... 0.007045000
Out Flow: ........... 1.2460679423999952
In Flow: ............ -1.2461052960000008
where each line was generated using
fillWString(L"string", 22, '.', L" ", L" ") << value;
Specifically, I was wondering what sort of tips you guys might give to improve this. Also, I was just wondering if copy-elision is guaranteed to occur here if the string is indeed padded?
c++ strings formatting
c++ strings formatting
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
edited 1 hour ago
Deduplicator
10.7k1849
10.7k1849
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked 3 hours ago
Slugger
1183
1183
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Slugger is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Named-value return elision isn't required by the standard, but it would be a poor implementation that didn't bother, at least when optimizing.
â Toby Speight
1 hour ago
add a comment |Â
Named-value return elision isn't required by the standard, but it would be a poor implementation that didn't bother, at least when optimizing.
â Toby Speight
1 hour ago
Named-value return elision isn't required by the standard, but it would be a poor implementation that didn't bother, at least when optimizing.
â Toby Speight
1 hour ago
Named-value return elision isn't required by the standard, but it would be a poor implementation that didn't bother, at least when optimizing.
â Toby Speight
1 hour ago
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
3
down vote
accepted
The point where the parameters stop and the function-body begins isn't easy to see.
Either indent more, or better consider changing to what c2-wiki calls form 6.
You are allocating way too much when passing arguments.
Using std::wstring_view instead of std::wstring const& lets you avoid that overhead and reduces indirection, which is also a win.
Also, you potentially re-allocate after copying the source when widening to the target-length.
static std::wstring fillWString(
std::wstring_view source,
size_t target,
wchar_t fillChar = L' ',
std::wstring_view startFill = ,
std::wstring_view endFill =
)
if (target <= source.size())
return source;
std::wstring result;
result.reserve(target);
result += source;
target -= source.size();
if (startFill.size() > target)
return result;
result += startFill;
target -= source.size();
if (endFill.size() > target)
result.append(target, fillChar);
return result;
result.append(target - endFill.size(), fillChar);
result += endFill;
return result;
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
add a comment |Â
up vote
2
down vote
Since we always make a copy of stringToFill, we could pass it by value, which will reduce the amount of copying when we use an rvalue as argument:
static std::wstring fillWString(
std::wstring stringToFill, ...
)
...
std::wstring& result(stringToFill);
...
return stringToFill;
Users can use std::move() if they pass a lvalue that's not required subsequently:
s = fillWString(std::move(s), 15);
Minor points:
- Whitespace is unusual - most C++ developers expect to see
&nestled against the type, rather than the value. - Consistently misspelt
std::size_t. - Default arguments of
L""could be written asif you find that more readable. - It's not clear from the description that if there's room for
initialFill, it will be used even if there's insufficient room forendFill. This may surprise users who want to use paired delimiters (e.g.[and]). - It's not clear from the description that if
initialFillandendFillexactly fit, there will be nofillCharinserted. That's easy enough to allow for if you know about it (just append the char toinitialFillor prepend it toendFill), but users need to be informed! - Why limit this to
std::wstring? A single template argument could make this general to all string types. A disadvantage to this as that template function arguments need to match exactly, preventing automatic conversions; workarounds for this include callers explicitly specifying the template instantiation, and/or providing a small family of forwarding functions. - Perhaps rearrange the logic to write just once to each character, rather than filling with
fillCharthen overwriting some of it.
Modified code
Here's how it looks with my suggestions applied; I've also shortened some of the variable names, which were over-long to my taste:
#include <string>
// Pad the supplied `str` to `width` characters long, using `fill`.
// If there's room for `prefix`, then use that to begin the padding;
// if there's also room for `suffix` then use that to end the padding.
// If `prefix` or `prefix+suffix` pad exactly, then no `fill` characters
// will be used - if at least one is required, add it to the end of
// `prefix`.
template<typename String>
String fillString(String str,
const typename String::size_type width,
const typename String::value_type fill = ' ',
const String& prefix = ,
const String& suffix = )
const auto originalLength = str.size();
if (originalLength >= width)
return str;
str.reserve(width);
if (originalLength + prefix.size() <= width)
// enough space for prefix
str += prefix;
if (str.size() + suffix.size() <= width)
// enough space for suffix as well
str.resize(width - suffix.size(), fill);
str += suffix;
else
str.resize(width, fill);
else
str.resize(width, fill);
return str;
// forwarding function, for convenience
template<typename CharT, typename... Args>
std::basic_string<CharT> fillString(std::basic_string<CharT> str, Args... rest)
return fillString<std::basic_string<CharT>>(std::move(str),
std::forward<Args>(rest)...);
And a demo program (note that std::literals::string_literals is a namespace that's intended to be imported wholesale, unlike std):
#include <iostream>
int main()
using namespace std::literals::string_literals;
static const auto s = L"FooBarBazQuux"s;
for (auto i = 5u; i < s.size(); ++i)
std::wcout << fillString(s.substr(0, i), 10, L'.', L" [.", L"]") << 'n';
Thanks for a nice answer!
â Slugger
36 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on thetraits_typeandallocator_type...
â Toby Speight
17 secs ago
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
The point where the parameters stop and the function-body begins isn't easy to see.
Either indent more, or better consider changing to what c2-wiki calls form 6.
You are allocating way too much when passing arguments.
Using std::wstring_view instead of std::wstring const& lets you avoid that overhead and reduces indirection, which is also a win.
Also, you potentially re-allocate after copying the source when widening to the target-length.
static std::wstring fillWString(
std::wstring_view source,
size_t target,
wchar_t fillChar = L' ',
std::wstring_view startFill = ,
std::wstring_view endFill =
)
if (target <= source.size())
return source;
std::wstring result;
result.reserve(target);
result += source;
target -= source.size();
if (startFill.size() > target)
return result;
result += startFill;
target -= source.size();
if (endFill.size() > target)
result.append(target, fillChar);
return result;
result.append(target - endFill.size(), fillChar);
result += endFill;
return result;
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
add a comment |Â
up vote
3
down vote
accepted
The point where the parameters stop and the function-body begins isn't easy to see.
Either indent more, or better consider changing to what c2-wiki calls form 6.
You are allocating way too much when passing arguments.
Using std::wstring_view instead of std::wstring const& lets you avoid that overhead and reduces indirection, which is also a win.
Also, you potentially re-allocate after copying the source when widening to the target-length.
static std::wstring fillWString(
std::wstring_view source,
size_t target,
wchar_t fillChar = L' ',
std::wstring_view startFill = ,
std::wstring_view endFill =
)
if (target <= source.size())
return source;
std::wstring result;
result.reserve(target);
result += source;
target -= source.size();
if (startFill.size() > target)
return result;
result += startFill;
target -= source.size();
if (endFill.size() > target)
result.append(target, fillChar);
return result;
result.append(target - endFill.size(), fillChar);
result += endFill;
return result;
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
add a comment |Â
up vote
3
down vote
accepted
up vote
3
down vote
accepted
The point where the parameters stop and the function-body begins isn't easy to see.
Either indent more, or better consider changing to what c2-wiki calls form 6.
You are allocating way too much when passing arguments.
Using std::wstring_view instead of std::wstring const& lets you avoid that overhead and reduces indirection, which is also a win.
Also, you potentially re-allocate after copying the source when widening to the target-length.
static std::wstring fillWString(
std::wstring_view source,
size_t target,
wchar_t fillChar = L' ',
std::wstring_view startFill = ,
std::wstring_view endFill =
)
if (target <= source.size())
return source;
std::wstring result;
result.reserve(target);
result += source;
target -= source.size();
if (startFill.size() > target)
return result;
result += startFill;
target -= source.size();
if (endFill.size() > target)
result.append(target, fillChar);
return result;
result.append(target - endFill.size(), fillChar);
result += endFill;
return result;
The point where the parameters stop and the function-body begins isn't easy to see.
Either indent more, or better consider changing to what c2-wiki calls form 6.
You are allocating way too much when passing arguments.
Using std::wstring_view instead of std::wstring const& lets you avoid that overhead and reduces indirection, which is also a win.
Also, you potentially re-allocate after copying the source when widening to the target-length.
static std::wstring fillWString(
std::wstring_view source,
size_t target,
wchar_t fillChar = L' ',
std::wstring_view startFill = ,
std::wstring_view endFill =
)
if (target <= source.size())
return source;
std::wstring result;
result.reserve(target);
result += source;
target -= source.size();
if (startFill.size() > target)
return result;
result += startFill;
target -= source.size();
if (endFill.size() > target)
result.append(target, fillChar);
return result;
result.append(target - endFill.size(), fillChar);
result += endFill;
return result;
answered 54 mins ago
Deduplicator
10.7k1849
10.7k1849
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
add a comment |Â
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
â Slugger
32 mins ago
add a comment |Â
up vote
2
down vote
Since we always make a copy of stringToFill, we could pass it by value, which will reduce the amount of copying when we use an rvalue as argument:
static std::wstring fillWString(
std::wstring stringToFill, ...
)
...
std::wstring& result(stringToFill);
...
return stringToFill;
Users can use std::move() if they pass a lvalue that's not required subsequently:
s = fillWString(std::move(s), 15);
Minor points:
- Whitespace is unusual - most C++ developers expect to see
&nestled against the type, rather than the value. - Consistently misspelt
std::size_t. - Default arguments of
L""could be written asif you find that more readable. - It's not clear from the description that if there's room for
initialFill, it will be used even if there's insufficient room forendFill. This may surprise users who want to use paired delimiters (e.g.[and]). - It's not clear from the description that if
initialFillandendFillexactly fit, there will be nofillCharinserted. That's easy enough to allow for if you know about it (just append the char toinitialFillor prepend it toendFill), but users need to be informed! - Why limit this to
std::wstring? A single template argument could make this general to all string types. A disadvantage to this as that template function arguments need to match exactly, preventing automatic conversions; workarounds for this include callers explicitly specifying the template instantiation, and/or providing a small family of forwarding functions. - Perhaps rearrange the logic to write just once to each character, rather than filling with
fillCharthen overwriting some of it.
Modified code
Here's how it looks with my suggestions applied; I've also shortened some of the variable names, which were over-long to my taste:
#include <string>
// Pad the supplied `str` to `width` characters long, using `fill`.
// If there's room for `prefix`, then use that to begin the padding;
// if there's also room for `suffix` then use that to end the padding.
// If `prefix` or `prefix+suffix` pad exactly, then no `fill` characters
// will be used - if at least one is required, add it to the end of
// `prefix`.
template<typename String>
String fillString(String str,
const typename String::size_type width,
const typename String::value_type fill = ' ',
const String& prefix = ,
const String& suffix = )
const auto originalLength = str.size();
if (originalLength >= width)
return str;
str.reserve(width);
if (originalLength + prefix.size() <= width)
// enough space for prefix
str += prefix;
if (str.size() + suffix.size() <= width)
// enough space for suffix as well
str.resize(width - suffix.size(), fill);
str += suffix;
else
str.resize(width, fill);
else
str.resize(width, fill);
return str;
// forwarding function, for convenience
template<typename CharT, typename... Args>
std::basic_string<CharT> fillString(std::basic_string<CharT> str, Args... rest)
return fillString<std::basic_string<CharT>>(std::move(str),
std::forward<Args>(rest)...);
And a demo program (note that std::literals::string_literals is a namespace that's intended to be imported wholesale, unlike std):
#include <iostream>
int main()
using namespace std::literals::string_literals;
static const auto s = L"FooBarBazQuux"s;
for (auto i = 5u; i < s.size(); ++i)
std::wcout << fillString(s.substr(0, i), 10, L'.', L" [.", L"]") << 'n';
Thanks for a nice answer!
â Slugger
36 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on thetraits_typeandallocator_type...
â Toby Speight
17 secs ago
add a comment |Â
up vote
2
down vote
Since we always make a copy of stringToFill, we could pass it by value, which will reduce the amount of copying when we use an rvalue as argument:
static std::wstring fillWString(
std::wstring stringToFill, ...
)
...
std::wstring& result(stringToFill);
...
return stringToFill;
Users can use std::move() if they pass a lvalue that's not required subsequently:
s = fillWString(std::move(s), 15);
Minor points:
- Whitespace is unusual - most C++ developers expect to see
&nestled against the type, rather than the value. - Consistently misspelt
std::size_t. - Default arguments of
L""could be written asif you find that more readable. - It's not clear from the description that if there's room for
initialFill, it will be used even if there's insufficient room forendFill. This may surprise users who want to use paired delimiters (e.g.[and]). - It's not clear from the description that if
initialFillandendFillexactly fit, there will be nofillCharinserted. That's easy enough to allow for if you know about it (just append the char toinitialFillor prepend it toendFill), but users need to be informed! - Why limit this to
std::wstring? A single template argument could make this general to all string types. A disadvantage to this as that template function arguments need to match exactly, preventing automatic conversions; workarounds for this include callers explicitly specifying the template instantiation, and/or providing a small family of forwarding functions. - Perhaps rearrange the logic to write just once to each character, rather than filling with
fillCharthen overwriting some of it.
Modified code
Here's how it looks with my suggestions applied; I've also shortened some of the variable names, which were over-long to my taste:
#include <string>
// Pad the supplied `str` to `width` characters long, using `fill`.
// If there's room for `prefix`, then use that to begin the padding;
// if there's also room for `suffix` then use that to end the padding.
// If `prefix` or `prefix+suffix` pad exactly, then no `fill` characters
// will be used - if at least one is required, add it to the end of
// `prefix`.
template<typename String>
String fillString(String str,
const typename String::size_type width,
const typename String::value_type fill = ' ',
const String& prefix = ,
const String& suffix = )
const auto originalLength = str.size();
if (originalLength >= width)
return str;
str.reserve(width);
if (originalLength + prefix.size() <= width)
// enough space for prefix
str += prefix;
if (str.size() + suffix.size() <= width)
// enough space for suffix as well
str.resize(width - suffix.size(), fill);
str += suffix;
else
str.resize(width, fill);
else
str.resize(width, fill);
return str;
// forwarding function, for convenience
template<typename CharT, typename... Args>
std::basic_string<CharT> fillString(std::basic_string<CharT> str, Args... rest)
return fillString<std::basic_string<CharT>>(std::move(str),
std::forward<Args>(rest)...);
And a demo program (note that std::literals::string_literals is a namespace that's intended to be imported wholesale, unlike std):
#include <iostream>
int main()
using namespace std::literals::string_literals;
static const auto s = L"FooBarBazQuux"s;
for (auto i = 5u; i < s.size(); ++i)
std::wcout << fillString(s.substr(0, i), 10, L'.', L" [.", L"]") << 'n';
Thanks for a nice answer!
â Slugger
36 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on thetraits_typeandallocator_type...
â Toby Speight
17 secs ago
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Since we always make a copy of stringToFill, we could pass it by value, which will reduce the amount of copying when we use an rvalue as argument:
static std::wstring fillWString(
std::wstring stringToFill, ...
)
...
std::wstring& result(stringToFill);
...
return stringToFill;
Users can use std::move() if they pass a lvalue that's not required subsequently:
s = fillWString(std::move(s), 15);
Minor points:
- Whitespace is unusual - most C++ developers expect to see
&nestled against the type, rather than the value. - Consistently misspelt
std::size_t. - Default arguments of
L""could be written asif you find that more readable. - It's not clear from the description that if there's room for
initialFill, it will be used even if there's insufficient room forendFill. This may surprise users who want to use paired delimiters (e.g.[and]). - It's not clear from the description that if
initialFillandendFillexactly fit, there will be nofillCharinserted. That's easy enough to allow for if you know about it (just append the char toinitialFillor prepend it toendFill), but users need to be informed! - Why limit this to
std::wstring? A single template argument could make this general to all string types. A disadvantage to this as that template function arguments need to match exactly, preventing automatic conversions; workarounds for this include callers explicitly specifying the template instantiation, and/or providing a small family of forwarding functions. - Perhaps rearrange the logic to write just once to each character, rather than filling with
fillCharthen overwriting some of it.
Modified code
Here's how it looks with my suggestions applied; I've also shortened some of the variable names, which were over-long to my taste:
#include <string>
// Pad the supplied `str` to `width` characters long, using `fill`.
// If there's room for `prefix`, then use that to begin the padding;
// if there's also room for `suffix` then use that to end the padding.
// If `prefix` or `prefix+suffix` pad exactly, then no `fill` characters
// will be used - if at least one is required, add it to the end of
// `prefix`.
template<typename String>
String fillString(String str,
const typename String::size_type width,
const typename String::value_type fill = ' ',
const String& prefix = ,
const String& suffix = )
const auto originalLength = str.size();
if (originalLength >= width)
return str;
str.reserve(width);
if (originalLength + prefix.size() <= width)
// enough space for prefix
str += prefix;
if (str.size() + suffix.size() <= width)
// enough space for suffix as well
str.resize(width - suffix.size(), fill);
str += suffix;
else
str.resize(width, fill);
else
str.resize(width, fill);
return str;
// forwarding function, for convenience
template<typename CharT, typename... Args>
std::basic_string<CharT> fillString(std::basic_string<CharT> str, Args... rest)
return fillString<std::basic_string<CharT>>(std::move(str),
std::forward<Args>(rest)...);
And a demo program (note that std::literals::string_literals is a namespace that's intended to be imported wholesale, unlike std):
#include <iostream>
int main()
using namespace std::literals::string_literals;
static const auto s = L"FooBarBazQuux"s;
for (auto i = 5u; i < s.size(); ++i)
std::wcout << fillString(s.substr(0, i), 10, L'.', L" [.", L"]") << 'n';
Since we always make a copy of stringToFill, we could pass it by value, which will reduce the amount of copying when we use an rvalue as argument:
static std::wstring fillWString(
std::wstring stringToFill, ...
)
...
std::wstring& result(stringToFill);
...
return stringToFill;
Users can use std::move() if they pass a lvalue that's not required subsequently:
s = fillWString(std::move(s), 15);
Minor points:
- Whitespace is unusual - most C++ developers expect to see
&nestled against the type, rather than the value. - Consistently misspelt
std::size_t. - Default arguments of
L""could be written asif you find that more readable. - It's not clear from the description that if there's room for
initialFill, it will be used even if there's insufficient room forendFill. This may surprise users who want to use paired delimiters (e.g.[and]). - It's not clear from the description that if
initialFillandendFillexactly fit, there will be nofillCharinserted. That's easy enough to allow for if you know about it (just append the char toinitialFillor prepend it toendFill), but users need to be informed! - Why limit this to
std::wstring? A single template argument could make this general to all string types. A disadvantage to this as that template function arguments need to match exactly, preventing automatic conversions; workarounds for this include callers explicitly specifying the template instantiation, and/or providing a small family of forwarding functions. - Perhaps rearrange the logic to write just once to each character, rather than filling with
fillCharthen overwriting some of it.
Modified code
Here's how it looks with my suggestions applied; I've also shortened some of the variable names, which were over-long to my taste:
#include <string>
// Pad the supplied `str` to `width` characters long, using `fill`.
// If there's room for `prefix`, then use that to begin the padding;
// if there's also room for `suffix` then use that to end the padding.
// If `prefix` or `prefix+suffix` pad exactly, then no `fill` characters
// will be used - if at least one is required, add it to the end of
// `prefix`.
template<typename String>
String fillString(String str,
const typename String::size_type width,
const typename String::value_type fill = ' ',
const String& prefix = ,
const String& suffix = )
const auto originalLength = str.size();
if (originalLength >= width)
return str;
str.reserve(width);
if (originalLength + prefix.size() <= width)
// enough space for prefix
str += prefix;
if (str.size() + suffix.size() <= width)
// enough space for suffix as well
str.resize(width - suffix.size(), fill);
str += suffix;
else
str.resize(width, fill);
else
str.resize(width, fill);
return str;
// forwarding function, for convenience
template<typename CharT, typename... Args>
std::basic_string<CharT> fillString(std::basic_string<CharT> str, Args... rest)
return fillString<std::basic_string<CharT>>(std::move(str),
std::forward<Args>(rest)...);
And a demo program (note that std::literals::string_literals is a namespace that's intended to be imported wholesale, unlike std):
#include <iostream>
int main()
using namespace std::literals::string_literals;
static const auto s = L"FooBarBazQuux"s;
for (auto i = 5u; i < s.size(); ++i)
std::wcout << fillString(s.substr(0, i), 10, L'.', L" [.", L"]") << 'n';
edited 2 mins ago
answered 43 mins ago
Toby Speight
20.6k436103
20.6k436103
Thanks for a nice answer!
â Slugger
36 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on thetraits_typeandallocator_type...
â Toby Speight
17 secs ago
add a comment |Â
Thanks for a nice answer!
â Slugger
36 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on thetraits_typeandallocator_type...
â Toby Speight
17 secs ago
Thanks for a nice answer!
â Slugger
36 mins ago
Thanks for a nice answer!
â Slugger
36 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
You know templating interferes with implicit-conversion? Still, one could template on the character-type, and let the caller disambiguate as needed. Or use a resolving forwarder / multiple forwarders to the single common implementation.
â Deduplicator
26 mins ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on the
traits_type and allocator_type...â Toby Speight
17 secs ago
I've added a forwarding function to convert arguments based on the string type. I resisted the temptation to also template it on the
traits_type and allocator_type...â Toby Speight
17 secs ago
add a comment |Â
Slugger is a new contributor. Be nice, and check out our Code of Conduct.
Slugger is a new contributor. Be nice, and check out our Code of Conduct.
Slugger is a new contributor. Be nice, and check out our Code of Conduct.
Slugger is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f206102%2ffancy-padding-of-wide-strings%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password

Named-value return elision isn't required by the standard, but it would be a poor implementation that didn't bother, at least when optimizing.
â Toby Speight
1 hour ago