I'm trying to overload the [] operator for a class that I created with different return types for the getter and setter.
I want the setter to return a reference to a another class instance, which is happening fine at the moment, the problem is that I want the getter to return a char, but in my main when i'm assigning char c = object[5] it's calling the setter instead of the getter and returning the wrong return type.
That how it looks in my code:
Board.h
const char& operator[](Board_Node index) const;
Board_Node& operator[](Board_Node index);
Board.cpp
const char& Board::operator[](Board_Node index) const
{
int boardIndex = index.i * _boardSize + index.j;
if (boardIndex < 0 || boardIndex >= _board.size())
{
throw IllegalCoordinateException(index.i, index.j);
}
return _board[index.i * _boardSize + index.j].token;
}
Board_Node & Board::operator[](Board_Node index)
{
int boardIndex = index.i * _boardSize + index.j;
if (boardIndex < 0 || boardIndex >= _board.size())
{
throw IllegalCoordinateException(index.i, index.j);
}
return _board[index.i * _boardSize + index.j];
}
main.cpp
char c = board1[{1, 2}]; cout << c << endl;
That line results in error: no suitable conversion function from "Board_Node" to "char" exists.
Already tried all forms with const everywhere and nothing worked.
Appericiate any help, thank you!
Calling the overloaded functions getters and setters based on whether an object is const or non-const is not appropriate.
The non-const version of the overload is given higher priority if the object is not const.
I suggest adding an explicitly named getter function, which can use the overloaded operator[] function.
char get(Board_Node index) const
{
return (*this)[index];
}
As a matter of good practice, it will be better to change the return type of the const version of the operator[] function to return Board_Node const&.
Board_Node const& operator[](Board_Node index) const;
Board_Node& operator[](Board_Node index);
That will allow you to extract other information from the corresponding Board_Node, not just a char.
With that, you won't need the 'getfunction. You'll have to change usage of theoperator[]` function a bit.
char c = board1[{1, 2}].token;
cout << c << endl;
At the catch site of a boost::exception (or std::exception), I want to iterate over all error_info elements of the exception, without knowing the types. I need to extract all the name-value pairs.
I guess it should possible since the boost::diagnostic_information function does that, but I'd like to avoid duplicating all that code.
Can this be done and how?
There's always the following information (iff you used BOOST_THROW_EXCEPTION):
char const * const * f=get_error_info<throw_file>(*be);
int const * l=get_error_info<throw_line>(*be);
char const * const * fn=get_error_info<throw_function>(*be);
if( !f && !l && !fn )
tmp << "Throw location unknown (consider using BOOST_THROW_EXCEPTION)\n";
Other than that you have use the error_info_container, but the data_ member is private¹.
If you are willing to "force" past that hurdle, the code to "duplicate" would not be so much:
char const *
diagnostic_information( char const * header ) const
{
if( header )
{
std::ostringstream tmp;
tmp << header;
for( error_info_map::const_iterator i=info_.begin(),end=info_.end(); i!=end; ++i )
{
error_info_base const & x = *i->second;
tmp << x.name_value_string();
}
tmp.str().swap(diagnostic_info_str_);
}
return diagnostic_info_str_.c_str();
}
Everything there is undocumented, not part of the public API though: it lives in namespace boost::exception_detail and the class boost::exception_detail::exception_info_container_impl.
In short, there be dragons (these interfaces are subject to change without notice and can depend on surprising assumptions).
¹ (except on some older compilers).
Say I want to write a vector-based string (String) just for the heck of it and I want an efficient c_str() operation on it.
Seems easy enough if I simply ensure the following:
//Make sure end() points to a '\0' and that '\0' is in allocated space
void afterEachNonConst() { reserve(size()+1); *(end()) = '\0'; }
Then c_str() is "just" begin() converted to const char*:
//Return C-string
const char* c_str() const { return (const char*)(&((*this)[0])); }
(I don't know how to do it shorter; the type system seems very unwilling to convert vector<char>::const_iterator to const char* even though they should be the same).
With that I want to override every non-const non-void method (except for and reserve) with:
auto ret = vector::method(arg1, arg2, arg3, ...);
//^can't it be just something like `auto ret = super();` ??
afterEachNonConst();
return ret;
and every non-const void method with:
vector::method(arg1, arg2, arg3, ...);
afterEachNonConst();
I guess there's no reasonably elegant way to just let C++ metaprogramming do all the work (?). Can I at least get a listing of all vector method signatures into my text editor somehow?
Here's a compilable example I played with:
#include <iostream>
#include <vector>
#include <cstring>
#include <cassert>
class String : public std::vector<char> {
public:
//Initialize from c-string
String& operator=(const char* cstr) {
size_t length = strlen(cstr);
reserve(strlen(cstr) + 1);
resize(length);
for(iterator ptr = begin(); *ptr++=*cstr++; ); //this will copy the '\0' too, but only at end()
return *this;
}
String(const char* cstr){ (*this) = cstr; }
//Return C-string
const char* c_str() const { return (const char*)(&((*this)[0])); }
void push_back(char value){
vector::push_back(value);
afterEachNonConst();
};
private:
//Make sure end() points to a '\0' and that '\0' is in allocated space
void afterEachNonConst() { reserve(size()+1); *(end()) = '\0'; }
};
int main(int argc, char **argv)
{
using namespace std;
String a = "foobar";
assert(a.size() == strlen(a.c_str()));
a.push_back('_');
a.push_back('1');
assert(a.size() == strlen(a.c_str()));
cout<<a.c_str()<<endl;
return 0;
}
What you want to do cannot be done with metaprogramming. You'd need reflection as part of the language, which is IMHO sadly a missing feature of C++.
To get a list of the member functions of std::vector I'd go and open the standard, N4431 §23.3.6 and try to extract them from there.
If this is too "much work" you could also try to implement something using libclang or libTooling, though this seems by far easier as it really is. (I just had to do something similar)
Since the syntax of C++ is so extremely complicated, especially in combination with templates, parsing it is really hard. Sadly using the above libraries it's also very hard to reproduce a parsed member function declaration.
The program fails while compiling the code. Compiler points to printf("Version = '%s'\n", gABXVER). I guess that I actually can't write gABXVER = "V1R1", but I don't have any other idea.
class CISPFVar_BINSTR : public CISPFVar
{
protected:
char* m_pBuffer;
long m_bDefined;
public:
...
void Initialize(char* szName, long lSize, int bDefineVar = 1)
{
Uninitialize();
ZStrToCharArray(szName, m_cName, 8);
m_Size = lSize+1;
m_pBuffer = (char*)malloc(m_Size);
m_pBuffer[0] = 0;
if (bDefineVar)
ISPLINK(__VDEFINE, m_cName, m_pBuffer, __BINSTR, &m_Size);
m_bDefined = bDefineVar;
}
...
};
CISPFVar_BINSTR gABXVER;
char szLoadLibraryPath[50];
int main(
int argc,
char* argv[])
{
if (argc > 1)
if (argv[1]) strcpy(szLoadLibraryPath, argv[1]);
gABXVER.Initialize("ABXVER",4);
gABXVER = "V1R1";
printf("Version = '%s'\n", gABXVER);
return 0;
};
When you use %s in printf family of functions, the corresponding argument type needs to be const char* or something that can be converted to const char*. The argument you are using is not such a type. Perhaps you meant to use:
printf("Version = '%s'\n", gABXVER.m_pBuffer);
The compiler should compile just fine (with possible warnings for printf) because printf doesn't care what you pass to it (beyond the first parameter) or whether it matches the format string. Modern compilers or error checking progs like lint will issue a warning if the params obviously don't match, and if you have a setting "treat warnings as errors", the prog may fail to compile.
That said, CISPFVar_BINSTR needs a public copy constructor if you want to pass it as a parameter by value to a function (because at least semantically a copy will be made). Does it have one? As others remarked it's customary to help your helpers by providing any information you have. Here we are badly missing the compiler errors. (You can edit your post at any time.)
I could imagine that the class has a conversion to char* or std::string, so it may suffice to try either printf("Version = '%s'\n", (char *)gABXVER) or printf("Version = '%s'\n", (std::string(gABXVER)).c_str() ).
You can only printf things that have format specifiers designed specifically for them. There is no format specifier that accepts a value of class type, so you cannot printf one directly.
The best thing you can do is explicitly convert your object to a const char* and pass the result to printf.
In c++ you can use many techniques to implement things like streaming operators
#include <iostream>
class Whatever
{
int value = 42;
public:
int Get() const {
return value;
}
friend std::ostream& operator<<(std::ostream&, Whatever const&);
};
std::ostream& operator<<(std::ostream& os, Whatever const& what) {
os << what.Get();
return os;
}
int main() {
Whatever x;
std::cout << x << std::endl;
}
printf is unsafe
In effect, you're doing serialization of your object into a readable string.
I am having difficulty writing my code in the way it should be written. This is my default constructor:
Address::Address() : m_city(NULL), m_street(NULL), m_buildingNumber(0), m_apartmentNumber(0)
{}
...and this is my other constructor:
Address::Address(const char* city, const char* street, const int buildingNumber,const int apartmentNumber) : m_city(NULL), m_street(NULL)
{
SetAddress(city,street,buildingNumber,apartmentNumber);
}
I have to initialize my city and street fields as they contain char * and my setter uses remove to set a new city for example. I would very much like to hear your opinion on how to write it in the right way without repeating code.
this is my SetAddress code :
bool Address::SetAddress(const char* city, const char* street, const int buildingNumber, const int apartmentNumber)
{
if (SetCity(city) == false || SetStreet(street) == false || SetBuildingNumber(buildingNumber) == false || SetApartmentNumber(apartmentNumber) == false)
return false;
return true;
}
and this is my SetCity:
bool Address::SetCity(const char* city)
{
if(city == NULL)
return false;
delete[] m_city;
m_city = new char[strlen(city)+1];
strcpy(m_city, city);
return true;
}
1 more question if i do change char* to string how can i check if string city doesnt equal to NULL as i know string does not have the "==" operator and string is an object and cannot be equal to null,
how can i check if the string i get is indeed legeal.
You should use std::string instead of C strings (const char*). Then you don't have to worry about having a "remove" function because std::string will manage the memory for you.
The only repeating code I see is the initializers. Since you should both be using initializers and cannot share initializers, some code redundancy is required here. I wouldn't worry about it.
When the new C++ comes out you'll be able to call the former constructor during initialization of the later. Until then, you'll just have to live with this minor smell.
You can combine the two ctors:
Address::Address(const char* city=NULL,
const char* street=NULL,
int buildingNumber=0,
int apartmentNumber=0)
: m_city(city),
m_street(street),
m_buildingNumber(buildingNumber),
m_apartmentNumber(apartmentNumber)
{}
[The top-level const on buildingNumber and apartmentNumber accomplished nothing and attempt to move implementation information into the interface, so I remove them.]
Of, if you really prefer:
Address::Address(const char* city=NULL,
const char* street=NULL,
int buildingNumber=0,
int apartmentNumber=0)
{
SetAddress(city,street,buildingNumber,apartmentNumber);
}
I generally prefer the former, but if SetAddress qualifies its inputs, it may be worthwhile. Of course, the suggestion to use std::string instead of pointers to char is a good one as well, but that's a more or less separate subject.
One other minor note: this does differ in one fundamental way from your original code. Your code required either 0 or 4 arguments to the ctor. This will accept anywhere from 0 to 4, arguments so a person could specify (for example) a city and street, but not a building number or apartment number. If it's really important to you that attempts at using 1, 2 or 3 arguments be rejected, this approach won't be useful to you. In this case, the extra flexibility looks like an improvement to me though -- for example, if somebody lives in a single-family dwelling, it's quite reasonable to omit an apartment number.
As answered by others (James McNellis' answer comes to mind), you should switch to std:string instead of char *.
Your problem is that repetition can't be avoided (both non default constructor and the setAddress method set the data), and having one calling the other could be less effective.
Now, the real problem, I guess, is that your code is doing a lot, which means that repetition of delicate code could be dangerous and buggy, thus your need to have one function call the other. This need can be remove by using the std::string, as it will remove the delicate code from your code altogether.
As it was not shown
Let's re-imagine your class:
class Address
{
public :
Address() ;
Address(const std::string & p_city
, const std::string & p_street
, int p_buildingNumber
, int p_apartmentNumber) ;
// Etc.
private :
std::string m_city ;
std::string m_street ;
int m_buildingNumber ;
int m_apartmentNumber ;
} ;
Using the std::string instead of the const char * will make the std::string object responsible for handling the resource (the string itself).
For example, you'll see I wrote no destructor in the class above. This is not an error, as without a destructor, the compiler will generate its own default one, which will handle the destructor of each member variable as needed. The remove you use for resource disposal (freeing the unused char *) is useless, too, so it won't be written. This means a lot of delicate code that won't be written, and thus, won't produce bugs.
And it simplifies greatly the implementation of the constructors, or even the setAddress method :
Address::Address()
// std::string are initialized by default to an empty string ""
// so no need to mention them in the initializer list
: m_buildingNumber(0)
, m_apartmentNumber(0)
{
}
Address::Address(const std::string & p_city
, const std::string & p_street
, int p_buildingNumber
, int p_apartmentNumber)
: m_city(p_city)
, m_street(p_street)
, m_buildingNumber(p_buildingNumber)
, m_apartmentNumber(p_apartmentNumber)
{
}
void Address::setAddress(const std::string & p_city
, const std::string & p_street
, int p_buildingNumber
, int p_apartmentNumber)
{
m_city = p_city ;
m_street = p_street ;
m_buildingNumber = p_buildingNumber ;
m_apartmentNumber = p_apartmentNumber ;
}
Still, there is repetition in this code, and indeed, we'll have to wait C++0x to have less repetition. But at least, the repetition is trivial, and easy to follow: No dangerous and delicate code, everything is simple to write and read. Which makes your code more robust than the char * version.
Your code looks good - it might be worthy to see the contents of SetAddress. I would highly recommend using std::string over char *s, if city and street aren't hard-coded into the program, which I doubt. You'll find std::string will save you headaches with memory-management and bugs, and will generally make dealing with strings much easier.
I might rewrite the setAddress() method as follows:
bool Address::setAddress(const char* city, const char* street, const int buildingNumber, const int apartmentNumber)
{
return (setCity(city)
&& setStreet(street)
&& setBuildingNumber(buildingNumber)
&& setApartmentNumber(apartmentNumber))
}
which will achieve the same short-circuiting and returning semantics, with a bit less code.
If you must use char * rather than std::string you need to manage the memory for the strings yourself. This includes copy on write when sharing the text or complete copy of the text.
Here is an example:
class Address
{
public:
Address(); // Empty constructor.
Address(const char * city,
const char * street,
const char * apt); // Full constructor.
Address(const Address& addr); // Copy constructor
virtual ~Address(); // Destructor
void set_city(const char * new_city);
void set_street(const char * new_street);
void set_apartment(const char * new_apartment);
private:
const char * m_city;
const char * m_street;
const char * m_apt;
};
Address::Address()
: m_city(0), m_street(0), m_apt(0)
{ ; }
Address::Address(const char * city,
const char * street,
const char * apt)
: m_city(0), m_street(0), m_apt(0)
{
set_city(city);
set_street(street);
set_apt(apt);
}
Address::Address(const Address& addr)
: m_city(0), m_street(0), m_apt(0)
{
set_city(addr.city);
set_street(addr.street);
set_apt(addr.apt);
}
Address::~Address()
{
delete [] m_city;
delete [] m_street;
delete [] m_apt;
}
void Address::set_city(const char * new_city)
{
delete [] m_city;
m_city = NULL;
if (new_city)
{
const size_t length = strlen(new_city);
m_city = new char [length + 1]; // +1 for the '\0' terminator.
strcpy(m_city, new_city);
m_city[length] = '\0';
}
return;
}
void Address::set_street(const char * new_street)
{
delete [] m_street;
m_street = NULL;
if (new_street)
{
const size_t length = strlen(new_street);
m_street = new char [length + 1]; // +1 for the '\0' terminator.
strcpy(m_street, new_street);
m_street[length] = '\0';
}
return;
}
void Address::set_apt(const char * new_apt)
{
delete [] m_apt;
m_apt = NULL;
if (new_apt)
{
const size_t length = strlen(new_apt);
m_apt = new char [length + 1]; // +1 for the '\0' terminator.
strcpy(m_apt, new_apt);
m_apt[length] = '\0';
}
return;
}
In the above example, the Address instance holds copies of the given text. This prevents problems when another entity points to the same text, and modifies the text. Another common issue is when the other entity deletes the memory area. The instance still holds the pointer, but the target area is invalid.
These issues are avoided by using the std::string class. The code is much smaller and easier to maintain. Look at the above code versus some of the other answers using std::string.