Implicit conversion and string::operator=() - c++

I have a problem with implicit conversion.
I have class ConfigValue and it contains two types of values - int and string. Yes, I need to store these two types, because config file contains some int an some string values.
This class has implemented two conversion operators:
ConfigValue::operator int() const {
if( m_type != TYPE_INT )
throw ConfigException( ConfigException::ERR_INVALID_TYPE, m_key );
return m_ival;
}
ConfigValue::operator std::string() const {
if( m_type != TYPE_STRING )
throw ConfigException( ConfigException::ERR_INVALID_TYPE, m_key );
return m_sval;
}
The next class is Config and it has map of values:<std::string, ConfigValue>. Sometimes I need to get some value, so it has implemented method get:
const ConfigValue & Config::get( const std::string& key) const {
auto pos = m_values.find( key );
if( pos == m_values.end() )
throw ConfigException( ConfigException::ERR_MISSING_KEY, key );
return m_values.at(key);
}
And my question and problem goes here:
// error: error: ambiguous overload for ‘operator=’ in ‘msg =
// in one of candidates:
// no known conversion for argument 1 from ‘const ConfigValue’ to ‘const char*’
string msg;
msg = config->get("DEFAULT_MSG");
// ok:
int port;
port = config->get("DEFAULT_PORT");
// ok:
string msg;
msg = static_cast<string>( config->get("DEFAULT_MSG") );
// ok:
string msg;
string tmp = config->get("DEFAULT_MSG");
msg = tmp;
So if I try to assign the value from get() to already created string object, it fails.
I tried to implement operator char*() but I got the same error and "no known conversion .." disappears.
Do I have to implement another conversion operator or something else to use it without static_cast?
Thanks :)

Isn't the need for those implicit conversions because of over-engineering with ConfigValue? It seems to be constructed without need for it. Without it you can get what you need directly from config and all seems more clear and type-safe:
string msg;
msg = config->getString("DEFAULT_MSG");
int port;
port = config->getInt("DEFAULT_PORT");

Try changing
ConfigValue::operator std::string() const
to
ConfigValue::operator const std::string&() const
If that doesn't solve your problem, you might be stuck adding explicit getters for the int and string types

Related

Non const field fine in a function but recognised as const in another one

I have a non const field ctxt.
And I have a funtion like this:
inventory_input inventory_selector::get_input()
{
inventory_input res;
const input_context ip;
res.action = ctxt.handle_input();
res.ch = ctxt.get_raw_input().get_first_input();
std::tuple<point, bool> tuple = ctxt.get_coordinates_inventory( w_inv );//Fine
std::tuple<point, bool> tuple = ip.get_coordinates_inventory( w_inv );//Error
res.entry = find_entry_by_invlet( res.ch );
if( res.entry != nullptr && !res.entry->is_selectable() ) {
res.entry = nullptr;
}
return res;
}
the error is "the object has type qualifiers that are not compatible with the member function" as ip is const but the funtion get_coordinates_inventory is not const.
However, I have a nother funtion like this:
inventory_entry *inventory_selector::find_entry_by_coordinate( point coordinate ) const
{
input_context ip;
std::tuple<point, bool> tuple = ctxt.get_coordinates_inventory( w_inv );//surprising, this line is having error.
std::tuple<point, bool> tuple = ip.get_coordinates_inventory( w_inv );//this line dosn't has error
}
The error message is:
the object has type qualifiers that are not compatible with the member function "input_context_inventory" object type is: const input_context.
I cannot understand why is this happening, both ctxt and ip are non const how can one of them having error while another one doesn't?
I have a non const field ctxt
...
the funtion get_coordinates_inventory is not const
There is your problem. Note the const qualifier on this method:
inventory_entry *inventory_selector::find_entry_by_coordinate( point coordinate ) const
^^^^^
This means that this method can be invoked on a const inventory_selector. Therefore, within this function the implicit this pointer points to const inventory_selector. Because of this, you can't invoke a non-const method on ctxt -- it is also const since it is a member of an object that is considered const within the context of the method:
this is const inventory_selector *.
So this->ctxt is const input_context &.
So the invocation this->ctxt.get_coordinates_inventory() is disallowed because that method is not declared const.
Either inventory_selector::find_entry_by_coordinate should be made non-const, or input_context::get_coordinates_inventory should be made const.

Elegant way to return different value types from function in c++ / c++11

i was looking in the Stack over flow to best way to return different value types from function in c++
i found few ways which are cool especially this method which is as close as it can be :
C++ same function parameters with different return type
but there is problem .
The Value Object can take/cast only strings so if i have something like this :
Value RetrieveValue(std::string key)
{
//get value
int value = get_value(key, etc);
return { value };
}
im getting :
error C2440: 'return': cannot convert from 'initializer list' to 'ReturnValue'
no suitable constructor exists to convert from "int" to "std::basic_string<char, std::char_traits<char>, std::allocator<char>>"
my question is can i modify Value object to support also bool and float and int?
struct Value
{
std::string _value;
template<typename T>
operator T() const //implicitly convert into T
{
std::stringstream ss(_value);
T convertedValue;
if ( ss >> convertedValue ) return convertedValue;
else throw std::runtime_error("conversion failed");
}
}
also why do the "value" are returned in : { value }
curly brackets??
std::string has no constructor taking an int alone. So you can't direct initialize a std::string with one.
You can make it compile with std::to_string, however
Value RetrieveValue(std::string key)
{
//get value
int value = get_value(key, etc);
return { std::to_string(value) };
}
To answer your questions in the comments:
{std::to_string(value)} aggregate initializes a Value object, the return value of your function.
The implicit conversion to any T happens outside of your function call. When the compiler need to assign the Value you returned to some variable, it looks for a proper conversion. Which the templated conversion operator provides.
Per your second comment. If you want to support only fundamental types, you can dispense of the exception in favor of a static_assert on std::is_fundamental:
template<typename T>
operator T() const //implicitly convert into T
{
static_assert(std::is_fundamental<T>::value, "Support only fundamental types");
std::stringstream ss(_value);
T convertedValue;
ss >> convertedValue
return convertedValue;
}

Error when Returning std::string from a function with return type of user defined String class

I have defined my own string class in "String.cpp" (class String), in some function with return type String, i am trying to return std::string, but it throws error Error:
Cannot use std::string to initialize PiaStd::String
Please help
String System::getHostName()
{
// in between code
std::string result;
struct addrinfo* p;
for(p = ppResult; p != 0; p=p->ai_next)
{
result += std::string(p->ai_canonname);
}
Freeaddrinfo(ppResult);
return result;
}
The problem is that the compiler does not know how to make your string from std::string. You need to provide a way to do it by writing one of the following:
Create a constructor of PiaStd::String that takes const std::string& as a parameter, or
Create a custom conversion operator from std::string to PiaStd::String
You could also change getHostName() to make your custom string, or to return std::string.

passing a std::ifstream to function as a pointer

I am sorry for the beginner question, but I do not understand what is going wrong with the ifstream. Is it not possible to send it to a function like a pointer (see below)?
The idea is that as a side effect I want the ifstream to move on while the function is being called, hence trying to send it as a pointer.
string ID, Title, Body;
ifstream ifs(filename); // std::string filename
while(ifs.good()) {
ID = findCell(ifs)
Title = findCell(ifs)
Body = findCell(ifs)
}
}
std::string findCell(ifstream *ifs) // changed to &
{
char c;
bool isPreviousQuote;
string str;
while(ifs.good())
{
ifs.read(c, 1); // error now shows up here
if (c == "\n") {
break;
}
str.push_back(c);
}
return str;
}
The error is:
invalid user-defined conversion from 'std::ifstream {aka std::basic_ifstream<char>}'
to 'std::ifstream* {aka std::basic_ifstream<char>*}' [-fpermissive]
Your function takes a pointer to std::ifstream object:
std::string findCell(ifstream *ifs)
Pointers should be initialized using the address of a memory block that they will point to.
In this case an address of ifs retrieved with &:
Title = findCell(&ifs);
Yet even better since findCell function requires the existence of the ifstream, it is much cleaner and more reasonable to pass by reference:
std::string findCell(std::ifstream& ifs) { ... }
...
Title = findCell(ifs);

Create String object from std::string by overloading = operator

I've tried several options but my compiler does not pick up the operator overloading or something else is wrong. I'm using XCode 4.5.2 with default Apple LLVM compiler 4.1.
The error I get is this: Assigning to 'cocos2d::CCString *' from incompatible type 'const char [5]'
on these lines:
CCString *s_piece__locks = "TEST";
cocos2d::CCString *s_piece__locks2 = "TEST";
My .h code:
CCString& operator= (const std::string& str);
// CCString& operator= (const char* str); // this doesn't work either
const CCString& operator = (const char *);
My .cpp code (even though this is irelevant):
CCString& CCString::operator= (const std::string& str)
{
m_sString = CCString::create(str)->m_sString;
return *this;
}
const CCString& CCString :: operator = (const char* str)
{
m_sString = CCString::create(str)->m_sString;
return *this;
}
Your help is very appreciated, thanks!
The error message Assigning to 'cocos2d::CCString *' from incompatible type 'const char [5]' suggests that you are assigning a char array to a pointer to cocos2d::CCString.
This should work:
char bar[] = "ABCD";
cocos2d::CCString foo;
foo = bar;
CCString *s_piece__locks = "TEST";
cocos2d::CCString *s_piece__locks2 = "TEST";
What the heck is this supposed to do? Declaring a pointer does not generate any object except the pointer itself. So basically, for this to "work", there would need to be another CCString object around already, that happens to represent the string "TEST". But even if that's given, how is C++ supposed to know which one to point to? It would need to look "TEST" up in some kind of e.g. hash map.
None of this makes any sense. Change your code to either
Direct use of object on stack:
cocos2d::CCString s_piece;
s_piece = "TEST";
Assigning new content to an object that resides somewhere else. You'd normally use a reference for this, e.g.
void assign_test_to(cocos2d::CCString& target) {
target = "TEST";
}
it's also possible with a pointer
void assign_test_to_ptr(cocos2d::CCString* target) {
*target = "TEST";
}
but don't do that unless you have a specific reason to.
In principle, there's another possibility:
cocos2d::CCString* s_piece_locks = new CCString;
*s_piece_locks = "TEST";
but you want to avoid this, as it can very easily lead to memory leaks. What would be ok is
std::unique_ptr<cocos2d::CCString> s_piece_locks = new CCString;
*s_piece_locks = "TEST";