Fancy padding of wide strings

The name of the pictureThe name of the pictureThe name of the pictureClash 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?










share|improve this question









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














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?










share|improve this question









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












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?










share|improve this question









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






share|improve this question









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.











share|improve this question









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.









share|improve this question




share|improve this question








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
















  • 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










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;






share|improve this answer




















  • Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
    – Slugger
    32 mins ago

















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 as if 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 for endFill. This may surprise users who want to use paired delimiters (e.g. [ and ]).

  • It's not clear from the description that if initialFill and endFill exactly fit, there will be no fillChar inserted. That's easy enough to allow for if you know about it (just append the char to initialFill or prepend it to endFill), 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 fillChar then 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';







share|improve this answer






















  • 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 the traits_type and allocator_type...
    – Toby Speight
    17 secs ago










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);






Slugger is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















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






























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;






share|improve this answer




















  • Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
    – Slugger
    32 mins ago














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;






share|improve this answer




















  • Thanks for the nice answer, including the use of std::wstring_view and changing the title of the post :D
    – Slugger
    32 mins ago












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;






share|improve this answer












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;







share|improve this answer












share|improve this answer



share|improve this answer










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
















  • 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












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 as if 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 for endFill. This may surprise users who want to use paired delimiters (e.g. [ and ]).

  • It's not clear from the description that if initialFill and endFill exactly fit, there will be no fillChar inserted. That's easy enough to allow for if you know about it (just append the char to initialFill or prepend it to endFill), 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 fillChar then 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';







share|improve this answer






















  • 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 the traits_type and allocator_type...
    – Toby Speight
    17 secs ago














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 as if 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 for endFill. This may surprise users who want to use paired delimiters (e.g. [ and ]).

  • It's not clear from the description that if initialFill and endFill exactly fit, there will be no fillChar inserted. That's easy enough to allow for if you know about it (just append the char to initialFill or prepend it to endFill), 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 fillChar then 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';







share|improve this answer






















  • 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 the traits_type and allocator_type...
    – Toby Speight
    17 secs ago












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 as if 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 for endFill. This may surprise users who want to use paired delimiters (e.g. [ and ]).

  • It's not clear from the description that if initialFill and endFill exactly fit, there will be no fillChar inserted. That's easy enough to allow for if you know about it (just append the char to initialFill or prepend it to endFill), 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 fillChar then 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';







share|improve this answer














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 as if 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 for endFill. This may surprise users who want to use paired delimiters (e.g. [ and ]).

  • It's not clear from the description that if initialFill and endFill exactly fit, there will be no fillChar inserted. That's easy enough to allow for if you know about it (just append the char to initialFill or prepend it to endFill), 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 fillChar then 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';








share|improve this answer














share|improve this answer



share|improve this answer








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 the traits_type and allocator_type...
    – Toby Speight
    17 secs 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











  • 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















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










Slugger is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















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.













 


draft saved


draft discarded














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













































































Comments

Popular posts from this blog

How to decode/decipher Mozilla Firefox proprietary .jsonlz4 format? (sessionstore-backups/recovery.jsonlz4)

White Anglo-Saxon Protestant

How to be valuable when automation & IT are stealing my job (and maybe my whole career)? [closed]