ostream for X, then generate for optional, unique_ptr, shared_ptr, possibly variant or vector
Given a type X which should be printed I naturally implementstd::ostream& operator<<(std::ostream& os, const X& x);
If I want to avoid repeating the work for various wrappers like std::optional<X>, std::unique_ptr<X>, std::shared_ptr<X> etc. what are my options? Are there libraries which implement this already? Given these keywords something like this is terribly hard to search for, the closed I could find was the Compile Time Type Information library, something I'd probably use to enhance the optional and vector outputs, but I want to avoid reinventing the wheel.
c++ iostream
add a comment |
Given a type X which should be printed I naturally implementstd::ostream& operator<<(std::ostream& os, const X& x);
If I want to avoid repeating the work for various wrappers like std::optional<X>, std::unique_ptr<X>, std::shared_ptr<X> etc. what are my options? Are there libraries which implement this already? Given these keywords something like this is terribly hard to search for, the closed I could find was the Compile Time Type Information library, something I'd probably use to enhance the optional and vector outputs, but I want to avoid reinventing the wheel.
c++ iostream
All the types you mentioned would need a differentoperator<<, since they might not have a value (you'd want to print something different/nothing for empty smart pointers and optionals).
– Pezo
Nov 13 '18 at 18:30
Yes, they would check for "empty", then just output "" or otherwise dereference the value and call the implemented operator and maybe add "uqX" - but that logic only needs to be written once for each wrapping type.
– finite graygreen
Nov 13 '18 at 18:37
add a comment |
Given a type X which should be printed I naturally implementstd::ostream& operator<<(std::ostream& os, const X& x);
If I want to avoid repeating the work for various wrappers like std::optional<X>, std::unique_ptr<X>, std::shared_ptr<X> etc. what are my options? Are there libraries which implement this already? Given these keywords something like this is terribly hard to search for, the closed I could find was the Compile Time Type Information library, something I'd probably use to enhance the optional and vector outputs, but I want to avoid reinventing the wheel.
c++ iostream
Given a type X which should be printed I naturally implementstd::ostream& operator<<(std::ostream& os, const X& x);
If I want to avoid repeating the work for various wrappers like std::optional<X>, std::unique_ptr<X>, std::shared_ptr<X> etc. what are my options? Are there libraries which implement this already? Given these keywords something like this is terribly hard to search for, the closed I could find was the Compile Time Type Information library, something I'd probably use to enhance the optional and vector outputs, but I want to avoid reinventing the wheel.
c++ iostream
c++ iostream
asked Nov 13 '18 at 18:14
finite graygreenfinite graygreen
27618
27618
All the types you mentioned would need a differentoperator<<, since they might not have a value (you'd want to print something different/nothing for empty smart pointers and optionals).
– Pezo
Nov 13 '18 at 18:30
Yes, they would check for "empty", then just output "" or otherwise dereference the value and call the implemented operator and maybe add "uqX" - but that logic only needs to be written once for each wrapping type.
– finite graygreen
Nov 13 '18 at 18:37
add a comment |
All the types you mentioned would need a differentoperator<<, since they might not have a value (you'd want to print something different/nothing for empty smart pointers and optionals).
– Pezo
Nov 13 '18 at 18:30
Yes, they would check for "empty", then just output "" or otherwise dereference the value and call the implemented operator and maybe add "uqX" - but that logic only needs to be written once for each wrapping type.
– finite graygreen
Nov 13 '18 at 18:37
All the types you mentioned would need a different
operator<<, since they might not have a value (you'd want to print something different/nothing for empty smart pointers and optionals).– Pezo
Nov 13 '18 at 18:30
All the types you mentioned would need a different
operator<<, since they might not have a value (you'd want to print something different/nothing for empty smart pointers and optionals).– Pezo
Nov 13 '18 at 18:30
Yes, they would check for "empty", then just output "" or otherwise dereference the value and call the implemented operator and maybe add "uqX" - but that logic only needs to be written once for each wrapping type.
– finite graygreen
Nov 13 '18 at 18:37
Yes, they would check for "empty", then just output "" or otherwise dereference the value and call the implemented operator and maybe add "uqX" - but that logic only needs to be written once for each wrapping type.
– finite graygreen
Nov 13 '18 at 18:37
add a comment |
1 Answer
1
active
oldest
votes
So, the basic idea is that you add a templated operator<< that would SFINAE away based on the return type of operator* of the object it gets passed. Something like this:
template <typename T, typename Expected>
using deref_to = std::is_same<std::decay_t<decltype(*std::declval<T>())>, Expected>;
template <typename T, typename = std::enable_if_t<deref_to<T, X>::value>>
std::ostream& operator<<(std::ostream &os, const T &foo)
if(!foo)
os << "";
else
os << *foo;
return os;
Live demo, this even works for raw pointers. Doesn't cover variant or vector, although that shouldn't be too difficult to add as well.
To handle std::variant, std::tuple, and std::pair you would probably do the same, just with std::get<X> instead of operator*.
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>
– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>
– finite graygreen
Nov 14 '18 at 17:07
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Just tested whether theoperator<<template is too greedy, doesn't seem like it.
– Pezo
Nov 14 '18 at 18:27
|
show 2 more comments
Your Answer
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: "1"
;
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',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
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
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287184%2fostream-for-x-then-generate-for-optionalx-unique-ptrx-shared-ptrx-poss%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
So, the basic idea is that you add a templated operator<< that would SFINAE away based on the return type of operator* of the object it gets passed. Something like this:
template <typename T, typename Expected>
using deref_to = std::is_same<std::decay_t<decltype(*std::declval<T>())>, Expected>;
template <typename T, typename = std::enable_if_t<deref_to<T, X>::value>>
std::ostream& operator<<(std::ostream &os, const T &foo)
if(!foo)
os << "";
else
os << *foo;
return os;
Live demo, this even works for raw pointers. Doesn't cover variant or vector, although that shouldn't be too difficult to add as well.
To handle std::variant, std::tuple, and std::pair you would probably do the same, just with std::get<X> instead of operator*.
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>
– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>
– finite graygreen
Nov 14 '18 at 17:07
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Just tested whether theoperator<<template is too greedy, doesn't seem like it.
– Pezo
Nov 14 '18 at 18:27
|
show 2 more comments
So, the basic idea is that you add a templated operator<< that would SFINAE away based on the return type of operator* of the object it gets passed. Something like this:
template <typename T, typename Expected>
using deref_to = std::is_same<std::decay_t<decltype(*std::declval<T>())>, Expected>;
template <typename T, typename = std::enable_if_t<deref_to<T, X>::value>>
std::ostream& operator<<(std::ostream &os, const T &foo)
if(!foo)
os << "";
else
os << *foo;
return os;
Live demo, this even works for raw pointers. Doesn't cover variant or vector, although that shouldn't be too difficult to add as well.
To handle std::variant, std::tuple, and std::pair you would probably do the same, just with std::get<X> instead of operator*.
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>
– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>
– finite graygreen
Nov 14 '18 at 17:07
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Just tested whether theoperator<<template is too greedy, doesn't seem like it.
– Pezo
Nov 14 '18 at 18:27
|
show 2 more comments
So, the basic idea is that you add a templated operator<< that would SFINAE away based on the return type of operator* of the object it gets passed. Something like this:
template <typename T, typename Expected>
using deref_to = std::is_same<std::decay_t<decltype(*std::declval<T>())>, Expected>;
template <typename T, typename = std::enable_if_t<deref_to<T, X>::value>>
std::ostream& operator<<(std::ostream &os, const T &foo)
if(!foo)
os << "";
else
os << *foo;
return os;
Live demo, this even works for raw pointers. Doesn't cover variant or vector, although that shouldn't be too difficult to add as well.
To handle std::variant, std::tuple, and std::pair you would probably do the same, just with std::get<X> instead of operator*.
So, the basic idea is that you add a templated operator<< that would SFINAE away based on the return type of operator* of the object it gets passed. Something like this:
template <typename T, typename Expected>
using deref_to = std::is_same<std::decay_t<decltype(*std::declval<T>())>, Expected>;
template <typename T, typename = std::enable_if_t<deref_to<T, X>::value>>
std::ostream& operator<<(std::ostream &os, const T &foo)
if(!foo)
os << "";
else
os << *foo;
return os;
Live demo, this even works for raw pointers. Doesn't cover variant or vector, although that shouldn't be too difficult to add as well.
To handle std::variant, std::tuple, and std::pair you would probably do the same, just with std::get<X> instead of operator*.
edited Nov 13 '18 at 18:58
answered Nov 13 '18 at 18:51
PezoPezo
969512
969512
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>
– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>
– finite graygreen
Nov 14 '18 at 17:07
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Just tested whether theoperator<<template is too greedy, doesn't seem like it.
– Pezo
Nov 14 '18 at 18:27
|
show 2 more comments
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>
– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>
– finite graygreen
Nov 14 '18 at 17:07
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Just tested whether theoperator<<template is too greedy, doesn't seem like it.
– Pezo
Nov 14 '18 at 18:27
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
Though that exactly does not compile (X/Expected is undefined), I now use the explicit specializations (Filler because otherwise the templates are seen as duplicates)...
– finite graygreen
Nov 14 '18 at 17:06
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename U = std::remove_reference_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::unique_ptr<U>> >, typename Filler1 = char>– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>– finite graygreen
Nov 14 '18 at 17:07
template <typename T, typename O = std::decay_t<decltype(*std::declval<T>())>, typename = std::enable_if_t< std::is_same_v<T, std::optional<O>>>, typename Filler1 = char, typename Filler2 = char>– finite graygreen
Nov 14 '18 at 17:07
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Look at the live demo, modulo copy paste errors it compiles exactly as posted. What error do you get?
– Pezo
Nov 14 '18 at 17:16
Just tested whether the
operator<< template is too greedy, doesn't seem like it.– Pezo
Nov 14 '18 at 18:27
Just tested whether the
operator<< template is too greedy, doesn't seem like it.– Pezo
Nov 14 '18 at 18:27
|
show 2 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
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
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287184%2fostream-for-x-then-generate-for-optionalx-unique-ptrx-shared-ptrx-poss%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
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
Required, but never shown
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
Required, but never shown
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
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
All the types you mentioned would need a different
operator<<, since they might not have a value (you'd want to print something different/nothing for empty smart pointers and optionals).– Pezo
Nov 13 '18 at 18:30
Yes, they would check for "empty", then just output "" or otherwise dereference the value and call the implemented operator and maybe add "uqX" - but that logic only needs to be written once for each wrapping type.
– finite graygreen
Nov 13 '18 at 18:37