Why is there no to_string(const string&)? - c++

I have template code which needs to convert some template type to string. For this I overload to_string for my own types. But the type can also be a string already. Then compilation fails, because there is no overload of to_string for type string itself (just returning its argument).
edit:
example code:
template<class T>
class TemplatedClass
{
public:
string toString() const
{
// this should work for both simple types like int, double, ...
// and for my own classes which have a to_string overload
// and also for string, which is the reason for my question
return string("TemplatedClass: ").append(to_string(t_));
}
private:
T t_;
};

You can just write your own templated function with proper overloads as follows:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
std::string toString(const T& t) {
return std::to_string(t);
}
std::string toString(const char* t) {
return t;
}
std::string toString(const std::string& t) {
return t;
}
int main() {
cout << toString(10) << endl;
cout << toString(1.5) << endl;
cout << toString("char*") << endl;
cout << toString(std::string("string")) << endl;
return 0;
}

You can just combine all std::to_string and all your to_string by using derective. And pass std::string by value to minimize number of copies:
#include <string>
#include <iostream>
namespace convert {
std::string to_string(std::string s)
{
return s;
}
template<class T>
std::string stringify(T&& t)
{
using convert::to_string;
using std::to_string;
return to_string(std::forward<T>(t));
}
}
class Foo
{
public:
operator std::string () const { return "Foo"; }
};
namespace bar {
class Bar
{};
std::string to_string(const Bar&) {
return "Bar";
}
}
int main()
{
std::string s{"I'm lvalue string"};
std::cout << convert::stringify(42) << "\n";
std::cout << convert::stringify(std::string("I'm string")) << "\n";
std::cout << convert::stringify("I'm c-string") << "\n";
std::cout << convert::stringify(s) << "\n";
std::cout << convert::stringify(Foo{}) << "\n";
std::cout << convert::stringify(bar::Bar{}) << "\n";
return 0;
}
Note that with my approach you don't need an overload for const char * or any other type that is convertible to a string. Also this approach allows a user to add to_string overload for any class (it will be found by argument-dependent lookup).
For further optimization convert::to_string accepting a string by value can be split into lvalue and rvalue overloads.

Since C++17 you can use constexpr in if statements.
You can use it as follows:
#include <iostream>
#include <string>
template<typename Ty>
std::string ToString(const Ty& value) {
if constexpr (std::is_convertible_v<Ty, std::string>) {
return value;
}
else {
return std::to_string(value);
}
}
int main() {
std::cout << ToString(123) << std::endl;
std::cout << ToString(123.0) << std::endl;
std::cout << ToString("char*") << std::endl;
std::cout << ToString(std::string("std::string")) << std::endl;
return 0;
}

Related

How can I get C++ to prefer converting char* to string_view instead of bool?

I have a simple program like this:
#include <iostream>
#include <string_view>
class C {
public:
void print(std::string_view v) { std::cout << "string_view: " << v << std::endl; }
void print(bool b) { std::cout << "bool: " << b << std::endl; }
};
int main(int argc, char* argv[]) {
C c;
c.print("foo");
}
When I run it, it prints bool: 1
How can I get C++ to prefer the string_view implicit conversion instead of the bool implicit conversion?
You can turn the string_view overload into a template function, and add a constraint to it so that it has a higher preference than the bool overload when it receives a type that can be converted to string_view.
#include <string_view>
class C {
public:
template<class T>
std::enable_if_t<std::is_convertible_v<const T&, std::string_view>>
print(const T& v) { std::cout << "string_view: " << std::string_view(v) << std::endl; }
void print(bool b) { std::cout << "bool: " << b << std::endl; }
};
Demo.

How to check programmatically string type in C++

How do I check if a string is of type std::string or is of type const char*?
template<typename B>
B check(B b)
{
//
}
check may be called with a std::string or a const char*
I'm not sure how to deduce the return type. How to determine if it should be std::string or const char*?
As I wrote in a comment, if you only have to deal with const char * and std::string maybe using a template is overkill.
However, if you really want to do it, you can use if constexpr:
#include <iostream>
#include <string>
#include <type_traits>
template<typename B>
B check(B b)
{
if constexpr (std::is_same_v<B, std::string>) {
std::cout << "std::string" << std::endl;
return b;
} else if constexpr (std::is_same_v<B, const char*>) {
std::cout << "const char *" << std::endl;
return b;
} else {
throw; // obviously you might want do to something different
}
}
int main() {
check("ciao");
check(std::string{"ciao"});
check(1); // throws
}
Also, you might want to guard before the if constexpr with a static assertion:
template<typename B>
B check(B b)
{
static_assert(std::is_convertible_v<const char *, B>); // just an idea
if constexpr (std::is_same_v<B, std::string>) {
std::cout << "std::string" << std::endl;
return b;
}
else if constexpr (std::is_same_v<B, const char*>) {
std::cout << "const char *" << std::endl;
return b;
} else {
std::cout << "other type convertible from `const char *`" << std::endl;
}
}
int main() {
check("ciao");
check(std::string{"ciao"});
//check(1); // fails at compile time
}

Is storing a reference to a (possibly) temporary object legal as long as the reference doesn't outlive the object?

After reading about std::variant and the handy overloaded trick I set myself a challenge (an exercise) to write a helper that would allow me to write
visit(variant).with([](int){}, [](char){});
instead of
std::visit(overloaded{[](int){}, [](char){}}, variant);
I'd also like it to work in other cases, like
VariantT variant;
VisitorT visitor;
visit(variant).with(visitor);
visit(VariantT{std::in_place_type<int>, 1}).with(visitor);
const VariantT constVariant;
visit(constVariant).with([](auto&&){});
You get the idea.
Here's what I came up with (note that I've changed the name from overloaded to Visitor:
#include <iostream>
#include <string>
#include <utility>
#include <variant>
template <typename... T>
struct Visitor : public T...
{
using T::operator()...;
};
template <typename... T>
Visitor(T...)->Visitor<T...>;
template <typename VariantT>
struct Helper
{
Helper(VariantT& variant)
: m_variant{variant}
{}
Helper(const Helper&) = delete;
Helper(Helper&&) = delete;
Helper& operator=(const Helper&) = delete;
Helper& operator=(Helper&&) = delete;
~Helper() = default;
template <typename VisitorT, typename... VisitorsT>
decltype(auto) with(VisitorT&& visitor,
VisitorsT&&... visitors) && // this function is ref-qualified so we can only call this on a temp object and we can
// be sure that the variant lives at least as long as us
{
if constexpr (sizeof...(visitors) == 0)
{
return std::visit(std::forward<VisitorT>(visitor), m_variant);
}
else
{
return std::visit(Visitor{visitor, visitors...}, m_variant);
}
}
private:
VariantT& m_variant;
};
template <typename VariantT>
decltype(auto) visit(VariantT&& variant)
{
// no forwarding here, even if an rvalue was passed, pass an lvalue ref
return Helper{variant};
}
int main()
{
visit(std::variant<int>{std::in_place_type<int>, -7})
.with([](int i) { std::cout << "got an int: " << i << std::endl; },
[](std::string str) { std::cout << "got a string: " << str << std::endl; });
std::variant<int, std::string> v = "String";
visit(v).with([](int i) { std::cout << "got an int: " << i << std::endl; },
[](std::string str) { std::cout << "got a string: " << str << std::endl; });
visit(v).with([](int& i) { i += 7; },
[](std::string& str) { str += "modified"; });
std::cout << visit(v).with([](int i) { return std::to_string(i); },
[](std::string str) { return str; }) << std::endl;
}
The question is: is storing references in the Helper like that perfectly legal?
My understanding is that temporary objects live until the end of the expression, so I guess this is ok?
Will it do The Right Thing? Are there any pitfalls in that code?
I've tested this code on both msvc and gcc and I'm not seeing anything wrong, but it doesn't mean this works fine in all cases.
The only obvious problem is that you can have things like
decltype(auto) silly() {
std::variant<int, std::string> v = "String";
return visit(v);
}
int main()
{
silly().with([](auto){ std::cout << "dangling" << std::endl; };
}
But such things are relatively easy to spot in review.

C++ - How to determine the type of the return value of a function?

I'm trying to build a vector of objects that have two properties, the key is always a String, and the value is always of type T
When I iterate through the vector, I need to be able to determine the type of the value property so that I can switch in a case statement to process it.
How do I determine the type of the return value of value get function of the vector object?
My class is here:
template <class T>
class IEFIAttribute
{
String key;
T value;
public:
IEFIAttribute(String key, T value)
{
this->key = key;
this->value = value;
}
String getKey();
T getValue();
};
template <class T>
String IEFIAttribute<T>::getKey()
{
return this->key;
}
template <class T>
T IEFIAttribute<T>::getValue()
{
return this->value;
}
And in my main.cpp, the following works:
...
IEFIAttribute <String> att("testkey","testvalue");
Serial.println("XXX Key: "+att.getKey());
Serial.println("XXX Value: "+att.getValue());
...
The result of running that is:
XXX Key: testkey
XXX Value: testvalue
What I want to be able to do is switch on the type of att.getValue() so that if it is a String, I do one thing, if it is xyz object, I process it according to my rules for xyz objects.
Any help will be very appreciated!
Kind regards!
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
using String = std::string;
template<class T>
class IEFIAttribute {
public:
using value_type = T; // add this to be able to query it later
IEFIAttribute(const String& Key, const T& Value) :
key(Key), // prefer using the member initializer list
value(Value)
{}
// instead of copies, return const references
String const& getKey() const {
return key;
};
T const& getValue() const {
return value;
}
private:
String key;
T value;
};
You have many possibilities to do special handling for certain types.
A template using constexpr if:
template<typename T>
T special_1(const std::vector<IEFIAttribute<T>>& v, size_t idx) {
if constexpr(std::is_same_v<T, String>) {
std::cout << "special_1() String handler\n";
} else if constexpr(std::is_same_v<T, int>) {
std::cout << "special_1() int handler\n";
} else {
std::cout << "special_1() generic handler\n";
}
return v[idx].getValue();
}
A template with specializations:
template<typename T>
T special_2(const std::vector<IEFIAttribute<T>>& v, size_t idx) {
std::cout << "special_2() generic handler\n";
return v[idx].getValue();
}
template<>
String special_2(const std::vector<IEFIAttribute<String>>& v, size_t idx) {
std::cout << "special_2() String handler\n";
return v[idx].getValue();
}
template<>
int special_2(const std::vector<IEFIAttribute<int>>& v, size_t idx) {
std::cout << "special_2() int handler\n";
return v[idx].getValue();
}
Or using the added value_type to do queries:
int main() {
std::vector<IEFIAttribute<String>> v1{{"testkey", "testvalue"}};
std::vector<IEFIAttribute<int>> v2{{"testkey", 10}};
// decltype(v1)::value_type is the vectors value_type
// and the second value_type is the one added to your class
if(std::is_same_v<decltype(v1)::value_type::value_type, String>) {
std::cout << "is String\n";
} else {
std::cout << "is not String\n";
}
if(std::is_same_v<decltype(v2)::value_type::value_type, int>) {
std::cout << "is int\n";
} else {
std::cout << "is not int\n";
}
std::cout << special_1(v1, 0) << "\n";
std::cout << special_1(v2, 0) << "\n";
std::cout << special_2(v1, 0) << "\n";
std::cout << special_2(v2, 0) << "\n";
}
One way to accomplish what you need is to use a typedef inside your class, and then functions of <type_traits> for you conditional statements.
For example:
template <class T>
class IEFIAttribute
{
public:
typedef T value_type;
// everything as before...
};
then
if (std::is_same<att::value_type, String>)
// .. do something

How to write a function wrapper for cout that allows for expressive syntax?

I'd like to wrap std::cout for formatting, like so:
mycout([what type?] x, [optional args]) {
... // do some formatting on x first
std::cout << x;
}
and still be able to use expressive syntax like
mycout("test" << i << endl << somevar, indent)
instead of being forced to be more verbose like
mycout(std::stringstream("test") << i ...)
How can I implement this? What type to make x?
Edit: added consideration for optional arguments
How about this:
struct MyCout {};
extern MyCout myCout;
template <typename T>
MyCout& operator<< (MyCout &s, const T &x) {
//format x as you please
std::cout << x;
return s;
}
And put MyCout myCout; into any one .cpp file.
You can then use myCout like this:
myCout << "test" << x << std::endl;
And it will call the template operator<< which can do the formatting.
Of course, you can also provide overloads of the operator for special formatting of specific types if you want to.
EDIT
Apparently (thanks to #soon), for standard manipulators to work, a few more overloads are necessary:
MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ostream &)) {
f(std::cout);
return s;
}
MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ios &)) {
f(std::cout);
return s;
}
MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ios_base &)) {
f(std::cout);
return s;
}
EDIT 2
I may have misunderstoor your original requirements slightly. How about this (plus the same manipulator overloads as above):
struct MyCout
{
std::stringstream s;
template <typename T>
MyCout& operator << (const T &x) {
s << x;
return *this;
}
~MyCout() {
somehow_format(s);
std::cout << s.str();
}
};
int main() {
double y = 1.5;
MyCout() << "test" << y;
}
This comes easy with variadic template arguments:
template <class T>
void print(T t)
{
std::cout << t;
}
template <class T, class... Args>
void print(T t, Args... args)
{
std::cout << t << std::endl;
print(args...);
}
int main()
{
std::cout << std::boolalpha;
print(3, 's', true, false);
}
Output:
3
s
true
false
Live Demo
A variation from the answers:
#include <iostream>
using namespace std;
class MyCout
{
public:
MyCout& operator()(bool indent) {
if ( indent ) cout << '\t';
return *this;
}
template<class T>
MyCout& operator<<(T t) {
cout << t;
return *this;
}
MyCout& operator<<(ostream& (*f)(ostream& o)) {
cout << f;
return *this;
};
};
int main()
{
MyCout mycout;
int x = 10;
mycout(true)<< "test" << 2 << x << endl ;
}
You can use this kind of class:
#include <iostream>
using namespace std;
class CustomOut
{
public:
template<class T>
CustomOut& operator<<(const T& obj)
{
cout << " my-cout " << obj;
return *this;
}
};
int main()
{
CustomOut mycout;
mycout << "test" << 4 << "\n" << 3.4;
}
You'd need more code to use std::endl and other functors, so i've used here simple \n instead.