operator << strange behaviour - c++

I have a class called Log, which overload the operator <<:
class Log
{
public:
static void init(std::ostream&);
Log(const std::string&);
~Log(); //Write to the log here
Log& operator<<(bool);
Log& operator<<(const std::string&);
private:
std::stringstream text;
static std::ostream *stream;
std::string tag;
};
Ok, here is the problem, when i write to the log like this:
int main()
{
std::ofstream file;
file.open("log.txt",std::ios::app);
Log::init(file);
Log("[INFO]") << "Test";
file.close();
}
The operator<< which receives a bool is called, that write true to the log..., if i delete the operator implementation which receives a bool then the other one is called correctly.
I think this happens because the char* can be interpreted as bool... but how can i fix it??

Create a third operator<< overload that takes a char * parameter.
I think your analysis of the problem is probably correct, although surprising.

There are two possible << operators, one taking a std::string, and one taking a bool. The first requires a user-defined conversion, constructing a std::string object from a char array. The second requires a standard conversion, converting a pointer to a bool (null pointer becomes false, non-null pointer becomes true). The rule here is that a standard conversion is better than a user-defined conversion (13.3.3.2 [over.ics.rank] /2), so the compiler chooses the bool version.

You may drop all operator << in your class and have a template:
template <typename T>
Log& operator << (Log& log, const T& value) {
// ...
return log;
}

Replace <<(bool) by <<(OnlyBool), with OnlyBool defined as:
struct OnlyBool
{
OnlyBool(bool b) : m_b(b){}
bool m_b;
};
The idea is to use a type that is implicitly created from bool, but only bool.
(Sorry for the terseness, I'm writing this on my phone)

Related

Calling << operator on types held in a std::variant?

I've got a struct like this:
// Literal.hpp
struct Literal
{
std::variant<
std::nullptr_t,
std::string,
double,
bool
>
value;
friend std::ostream &operator<<(std::ostream &os, Literal &literal);
};
and I'm trying to implement the << operator like this:
// Literal.cpp
Literal::Literal() : value(value) {}
std::ostream &operator<<(std::ostream &os, const Literal &literal)
{
std::visit(/* I don't know what to put here!*/, literal.value);
}
I've tried implementing the operator like this (note: I would take any elegant solution it doesn't have to be a solution to this implementation below)
// In Literal.cpp
std::ostream &operator<<(std::ostream &out, const Literal literal)
{
std::visit(ToString(), literal.value);
return out;
}
struct ToString; // this declaration is in literal.hpp
void ToString::operator()(const std::nullptr_t &literalValue){std::cout << "null";}
void ToString::operator()(const char &literalValue){std::cout << std::string(literalValue);}
void ToString::operator()(const std::string &literalValue){std::cout << literalValue;}
void ToString::operator()(const double &literalValue){std::cout << literalValue;}
void ToString::operator()(const bool &literalValue){std::cout << literalValue;}
But in my main function, passing a char array literal doesn't casts it into a bool when it runs! ignoring the operator overload taking a char:
main() {
Literal myLiteral;
myLiteral.value = "Hello World";
std::cout << myLiteral << std::endl;
}
This is a bug in your standard library. Presumably you're using libstc++ (the GNU C++ standard library), since that's what Godbolt shows as messing up. If you compile with libc++ (Clang/LLVM's C++ standard library), this works as expected. According to std::vector<Types...>::operator=(T&& t)'s cppreference page, it
Determines the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types... in scope at the same time, except that:
An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable x;
If T_i is (possibly cv-qualified) bool, F(T_i) is only considered if std:remove_cvref_t<T> is also bool.
That last clause is there for this very situation. Because lots of things can convert to bool, but we don't usually intend this conversion, that clause causes conversion sequences that would not normally be selected to be selected (char const* to bool is a standard conversion, but to std::string is "user-defined", which is normally considered "worse"). Your code should set value to its std::string alternative, but your library's implementation of std::variant is broken. There's probably an issue ticket already opened, but if there isn't, this is grounds to open one. If you're stuck with your library, explicitly marking the literal as a std::string should work:
literal.value = std::string("Hello World");
For the elegance question, use an abbreviated template lambda.
std::ostream &operator<<(std::ostream &os, Literal const &literal)
{
std::visit([](auto v) { std::cout << v; }, literal.value);
// or
std::visit([](auto const &v) {
// gets template param vvvvvvvvvvvvvvvvvvvvvvvvv w/o being able to name it
if constexpr(std::is_same_v<std::decay_t<decltype(v)>, std::nullptr_t>) {
std::cout << "null";
} else std::cout << v;
}, literal.value);
// only difference is nullptr_t => "nullptr" vs "null"
return std::cout;
}
Also, your friend declaration doesn't match the definition. Actually, it shouldn't be friended anyway, since it needs no access to private members.
// declaration in header, outside of any class, as a free function
std::ostream &operator<<(std::ostream&, Literal const&);
// was missing const ^^^^^

Operator overloading c++ (<<)

This the below program i have written for some test.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
void operator<<(const char *str1,const char *str2)
{
tgsetlist.insert( std::map<std::string,std::string>::value_type(str1,str2));
}
};
int main()
{
tgsetmap obj;
obj<<("tgset10","mystring");
obj.tgsetlist.size();
}
This throws a compilation error:
"test.cc", line 10: Error: Illegal number of arguments for tgsetmap::operator<<(const char, const char*).
"test.cc", line 22: Error: The operation "tgsetmap << const char*" is illegal.
2 Error(s) detected.*
Am i wrong some where?
You can't force operator<< to take two arguments on right-hand side. The following code:
obj<<("tgset10","mystring");
does not work as a function call with two arguments but instead just uses the , operator. But it's probably not what you are interested in.
If you need to pass two arguments to the << operator, you need to wrap them in some other (single) type. For example, you could use the standard std::pair, i.e. std::pair<const char*, const char*>.
But note that the operator<< should also return some reasonable type suitable for << chaining. That would probably be a tgsetmap& in your case. The following version should work fine:
#include <map>
#include <string>
#include <iostream>
class tgsetmap
{
public:
typedef std::map<std::string, std::string> list_type;
typedef list_type::value_type item_type;
list_type tgsetlist;
tgsetmap& operator<<(item_type item)
{
tgsetlist.insert(item);
return *this;
}
};
int main()
{
tgsetmap obj;
obj << tgsetmap::item_type("tgset10","mystring")
<< tgsetmap::item_type("tgset20","anotherstring");
std::cout << obj.tgsetlist.size() << std::endl;
}
Note that I've added typedefs to not have to repeat the type names over and over again. I've also made operator<< return a tgsetmap& so that << could be chained (used like in the modified main() above). And finally, I've reused the std::map<...>::value_type to make it simpler but you could also use any other type of your own.
But I believe that you may prefer using a regular method instead. Something like:
void add(const char *str1, const char *str2)
{
tgsetlist.insert( std::map<std::string, std::string>::value_type(str1, str2));
}
(inside the class declaration), and then:
obj.add("tgset10", "mystring");
The operator<< inside of a class must be overloaded like this:
T T::operator <<(const T& b) const;
If you want to overload it with 2 arguments, you can do it outside of a class:
T operator <<(const T& a, const T& b);
My compiler, for example, gives a more detailed error message for the code you posted:
If you are not sure about an operator overloading syntax, there is a wiki article about it.
Yes. operator << is binary operator. not ternary. not forget about this pointer.
As mentioned, the << is binary operator, so there is no way it can take more than two args(One should be this if you are declaring inside the class or a LHS if you are declaring outside the class). However you can accomplish the same functionality by doing obj<<"tgset10". <<"mystring";. But since << is a binary operator, you have to do some hack for this.
For this, I ve assigned a static variable op_count, where in I will determine if it is the value or the type. And another static variable temp_str to store the previous value across invocations.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
static int op_count = 0;
static const char *temp_str;
tgsetmap& operator<<(const char *str)
{
op_count++;
if (op_count%2 != 0) {
temp_str = str;
}
else {
tgsetlist.insert( std::map<std::string,std::string>::value_type(temp_str,str));
}
return this;
}
};
So you can do
int main()
{
tgsetmap obj;
obj<<"tgset10"<<"mystring";
obj.tgsetlist.size();
}
Or simply you can embed the value and type in the same string using some separator,
value:type = separator is :
value_type = separator is _.

Explicit conversion and templated conversion operator

I wanted to somehow extend the Microsoft type _variant_t so it accepts implicit/explicit conversions to/from additional types. To do so, I wrote the following class:
class value_type
{
public:
/* Constructors */
value_type(const std::string& str) : m_variant(str.c_str()) {}
template <typename Type> value_type(const Type& value) : m_variant(value) {}
/* Conversion operators */
operator const _variant_t&() const { return m_variant; }
operator std::string() const { return static_cast<const char*>(m_variant); }
template <typename Type> operator Type() const { return static_cast<Type>(m_variant); }
private:
_variant_t m_variant;
};
That is, if every instance of _variant_t in the code is replaced with value_type, it "works" the same.
Lets consider the following function, which returns a _variant_t:
_variant_t foo();
If I write:
std::string bar()
{
value_type v = foo();
return v;
}
It compiles just fine.
But if I change the previous code like that:
std::string bar()
{
return value_type(foo());
}
or:
std::string bar()
{
return static_cast<std::string>(value_type(foo()));
}
The compilation fails with the following message:
configuration.cpp(41) : error C2668: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string' : ambiguous call to overloaded function
If I remove the template <typename Type> operator Type... line, everything compiles.
Now I understand what the compilater says (it doesn't know which operator to use) but I don't understand why: It would seem logical to use the operator std::string when converting to std::string. What am I missing ?
Thank you.
The problem is that string's constructor is overloaded. So if returning involves invoking a string constructor, then there are multiple choices: argument could be converted to const char*, allocator<char> or string.
static_cast<string>(x) is the same as string(x).
I suppose your first faulty example should read return string(foo());, not return value_type(foo());

How to create a "default" stream insertion operator in C++?

I have a class similar to boost::any, in that it is a templated container class. I'd like to have a method to write the contained value to a string. However, if the contained type doesn't provide a stream insertion operator, I'd like my method to return some default tring rather than failing to compile. Below is as close as I've come, and should make it clear as to what I'm trying to do:
namespace W {
namespace hide {
template <typename T>
std::ostream& operator<<(std::ostream& out, const T& t) {
return std::operator<<(out, typeid(T).name());
}
}
template <typename T> struct C {
T t_;
std::string ToString() const {
using namespace hide;
std::ostringstream oss;
oss << t_;
return oss.str();
}
};
}
This works pretty well, with some caveats. For example, if I want to actually provide an overloaded insertion operator for a class, then that operator has to either be in the same namespace as the class, or it has to be in the W namespace for it to be considered.
It also has problems with any type that already has a non-member std::operator<<, e.g. char and std::string. If T is one of those types, then the oss << t_ line above becomes ambiguous. This can be worked around by adding overloads for these types inside the W namespace, for example:
std::ostream& operator << (std::ostream& out, const std::string& s) {
return std::operator <<(out, s);
}
My question is, has anyone found a better method than this? Why do I have to add my own overloads for things like std::string? Is this all supported according to the standard, or am I taking advantage of non-standard behavior? (I am testing with g++ 4.3.3)
Below is some code that I recall seeing a while ago in a compiler construction class. I thought it was particularly clever (if not 'clean') so I held on to it.
From http://www.cs.colorado.edu/~main/a++/tree.h
// If data of type T can be printed with the usual << operator, then
// print<T>(out, p) will interpret *p as a T object and print its
// value to out. Otherwise, a message is printed to out, indicating
// that objects of type T are not printable.
template<typename T> void print(std::ostream& out, const void* p)
{
// The first part of this code sets an enum value, is_printable, to
// be 1 if the data type T can be printed with the usual <<
// operator. Otherwise, is_printable is set to zero. The programming
// technique is based on a note from Herb Sutter at
// http://www.gotw.ca/gotw/071.htm
class object
{
public:
object(T convert) { };
};
char operator << (std::ostream&, const object&);
enum { is_printable = sizeof(std::cout << (*static_cast<T*>(0))) == sizeof(char) ? 0 : 1 };
// Notice that the boolean expression in the if-statement is known at
// compile time, so that only one of the two output statements will be
// compiled into object code.
if (is_printable)
out << *static_cast<const T*>(p);
else
out << "(value of type " << typeid(T).name() << " cannot be printed)";
}
When you construct your container object, hold a pointer to the print function for the variable:
void (*printer)(std::ostream&, const void*);
printer = print<T>;
Then later use the printer() function to display the contained value if possible.
Hope this helps.

Unable to find operator via implicit conversion in C++

When writing a class to act as a wrapper around a heap-allocated object, I encountered a problem with implicit type conversion that can be reduced to this simple example.
In the code below the wrapper class manages a heap-allocated object and implicitly converts to a reference to that object. This allows the wrapper object to be passed as the argument to the function write(...) since implicit conversion takes place.
The compiler fails, however, when trying to resolve the call to operator<<(...), unless an explicit cast is made (checked with MSVC8.0, Intel 9.1 and gcc 4.2.1 compilers).
So, (1) why does the implicit conversion fail in this case? (2) could it be related to argument-dependent lookup? and (3) is there anything that can be done to make this work without the explicit cast?
#include <fstream>
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
void write(std::ostream& os)
{
os << "(1) Hello, world!\n";
}
int main()
{
wrapper<std::ostream> file(new std::ofstream("test.txt"));
write(file);
static_cast<std::ostream&>( file ) << "(2) Hello, world!\n";
// file << "(3) This line doesn't compile!\n";
}
It fails because you're trying to resolve an operator of your wrapper<T> class that doesn't exist. If you want it to work without the cast, you could put together something like this:
template<typename X> wrapper<T> &operator <<(X &param) const {
return t << param;
}
Unfortunately I don't know of a way to resolve the return type at compile time. Fortunately in most cases it's the same type as the object, including in this case with ostream.
EDIT: Modified code by suggestion from dash-tom-bang. Changed return type to wrapper<T> &.
The compiler doesn't have enough context to determine that operator& will make a valid conversion. So, yes, I think this is related to argument-dependent lookup: The compiler is looking for an operator<< that can accept a non-const wrapper<std::ostream> as its first parameter.
I think the problem has to do with maintaining some compile-time constraints. In your example, the compiler first would have to find all the possible operator<<. Then, for each of them, it should try if your object could be automatically converted (directly or indirectly) to any of the types that each operator<< are able to accept.
This test can be really complex, and I think this is restricted to provide a reasonable compiling time.
After some testing, an even simpler example identifies the source of the problem. The compiler cannot deduce the template argument T in f2(const bar<T>&) below from implicit conversion of wrapper<bar<int> > to bar<int>&.
template <typename T>
class wrapper
{
T* t;
public:
explicit wrapper(T * const p) : t(p) { }
~wrapper() { delete t; }
operator T & () const { return *t; }
};
class foo { };
template <typename T> class bar { };
void f1(const foo& s) { }
template <typename T> void f2(const bar<T>& s) { }
void f3(const bar<int>& s) { }
int main()
{
wrapper<foo> s1(new foo());
f1(s1);
wrapper<bar<int> > s2(new bar<int>());
//f2(s2); // FAILS
f2<int>(s2); // OK
f3(s2);
}
In the original example, std::ostream is actually a typedef for the templated class std::basic_ostream<..>, and the same situation applies when calling the templated function operator<<.
Check the signature of the insertion operator... I think they take non-const ostream reference?
Confirmed with C++03 standard, signature of the char* output operator is:
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
which does indeed take a non-const reference. So your conversion operator does not match.
As noted in the comment: this is irrelevant.
There are various limits in the Standard about conversions applied... maybe this would need to implicit conversions (your operator, and cast to base type) when at most one should be applied.