Overloading, strings, and default parameters - c++

Refactoring legacy code, I came across this function (pseudocode):
int getMessage( char * buffer, int size = 300 );
Gee, look at that buffer just waiting to overflow. So I came up with a function using std::string, and thought it would be nice to use function overloading:
int getMessage( std::string & buffer );
So far, so good. But when I try to call the function with a string:
std::string buffer;
int rc = getMessage( buffer );
I get this error:
cannot convert 'std::string' to 'char*' for argument '1' to 'int getMessage(char*, int)'
Obviously, the compiler (GCC 4.1.2) tries hard to convert std::string to char* to satisfy the first function's parameter list (using the default value to satisfy the second parameter), gives up, but doesn't try the second function...
I wouldn't have a problem working around this issue, but I'd like to know why this fails, and whether there would be a way to make it work as intended.

It works as expected on my GCC 4.3.2, maybe you misspelled the name of the overload? There's no conversion from std::string to char*, so the compiler shouldn't have any problems choosing the correct overload.
$ cat test.cpp
#include <string>
#include <stdio.h>
int getMessage( char * buffer, int size = 300 )
{
printf("1\n");
return 1;
}
int getMessage( std::string & buffer )
{
printf("2\n");
return 2;
}
int main()
{
std::string buffer;
buffer = "Hello";
int rc = getMessage( buffer );
}
$ g++ test.cpp -Wall -pedantic
test.cpp: In function ‘int main()’:
test.cpp:20: warning: unused variable ‘rc’
$ ./a.out
2
$ $ g++ -v 2>&1|tail -n1
gcc version 4.3.2 (Ubuntu 4.3.2-1ubuntu12)
$

Hmmm. There is no implicit conversion from std::string to char*, so that can't be your problem. Are you sure your new function is visible at the call site?
You said this is pseudo-code. Are you leaving something out? Are these template functions or member functions? Please post more of the code or try to boil it down to a smaller test case.

My guess is that the overload for the string version of the function isn't visible where you called it. Are you sure that it is in the correct header file, and is spelled correctly?

Do you have a declaration of `int getMessage( std::string & buffer );' in scope? You are hitting this error because the proper function is not being found.

As always, once the problem is solved, the solution is painfully trivial and should have been obvious all along.
So I came up with a function using std::string...
...in my working directory, which compiled just fine, but -I and -L in my makefile were still pointing at the previous version of the library, which was blissfully unaware of the new function.
Sorry for the bother. I've been an idiot. I hope this doesn't become a habit. ;-)

Related

Missing an error when incorrectly passing string to printf-style log function

It is very common for any medium-to-large project to replace printf with a custom log function. Here is a minimal C++ example and its usage:
#include <stdio.h>
#include <stdarg.h>
#include <string>
void log_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap); // real code obviously does something fancier
va_end(ap);
}
int main() {
std::string x = "Hello";
// correct code
printf("String is %s\n", x.c_str());
log_printf("String is %s\n", x.c_str());
// incorrect code
printf("String is %s\n", x); // bad line 1
log_printf("String is %s\n", x); // bad line 2
}
The simple logger receives a variable amount of arguments and calls vprintf to output them to standard output. The lines under 'correct code' demonstrate correct usage of this logger. My question involves the 'bad' lines, where a string object is incorrectly passed instead of a pointer to the character buffer.
Under GCC 4.6 (tested under Linux) neither of the bad lines can compile, which is a good thing because I want to catch such incorrect usage. The error is:
error: cannot pass objects of non-trivially-copyable type ‘std::string {aka struct std::basic_string<char>}’ through ‘...’
However in GCC 5.1 it has apparently become possible to pass non-trivially-copyable objects, and the compilation succeeds. If I use -Wall then only 'bad line 1' raises a warning about an unexpected argument type, but 'bad line 2' with the log_printf compiles without issue in any case. Needless to say both lines produce garbage output.
I can catch 'bad line 1' with -Wall -Werror, but what about 'bad line 2'? How can I cause it to also generate a compilation error?
For your own functions you need to use a common function attribute call format:
void log_printf(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
void log_printf(const char* fmt, ...) {
...
}
Note that the attribute must be set on a function declaration, not the definition.
The first argument to the format attribute is the style, in this case printf (scanf is also possible, for functions that works like scanf), the second argument is the format string, and the third argument is where the ellipsis ... is. It will help GCC check the format strings like for the standard printf function.
This is a GCC extension, though some other compilers have adopted it to become GCC compatible, most notably the Intel C compiler ICC and Clang (the standard compiler used on OSX and some BSD variants). The Visual Studio compiler does not have this extension, and I know of no similar thing for Visual C++.

How to wrap a C++ function?

I'm trying to wrap a c++ function called i_receive() by following this tutorial, I first created a wrap.c file, the content of this file is like this:
int i_receive(const uint32_t *f, int32_t t){
static int (*real_i_receive)(const uint32_t *, int32_t)=NULL;
printf("hello world");
return real_i_receive;
}
I compiled this file with gcc -fPIC -shared -o wrap.so wrap.c -ldl, when I used the LD_PRELOAD to run some C++ code with LD_PRELOAD=/full/path/to/wrap.so ./mycppcode I got this message:
ERROR: ld.so: object '/full/path/to/wrap.so' from LD_PRELOAD cannot be preloaded: ignored`.
I was guessing the reason might be that the wrap file is a C file, and I'm using it with C++ code, am I right?
I changed the file to wrap.cc with the same content, when compiling in the same way as before, I got:
ERROR: invalid conversion from 'int (*)(const uint32_t*, int32_t)' to 'int'
First of all, your 2nd error your are getting becase you are returning a Pointer to function type instead of a int type.
If you want to return an int, call the function from the code :
return real_i_receive(f,t);
Notice the "()" which means a function call.
Regarding your guess : it doesn't matter if you are using C or C++ code, the libaries are all assembly code.
One difference between exporting C functions and C++ functions is the name mangling. You would rather export a function as a C function to be able to access it inside your library through unmagled name.
To export a function without name mangling it, you can use extern "C" .
Replace
return real_i_receive;
with
return real_i_receive(f, t);
As it is, the return type of your function is int but you're returning a function pointer.

Why is GCC tricked into allowing undefined behavior simply by putting it in a loop?

The following is nonsensical yet compiles cleanly with g++ -Wall -Wextra -Werror -Winit-self (I tested GCC 4.7.2 and 4.9.0):
#include <iostream>
#include <string>
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const std::string& str = str; // !!
std::cout << str << std::endl;
}
}
The line marked !! results in undefined behavior, yet is not diagnosed by GCC. However, commenting out the for line makes GCC complain:
error: ‘str’ is used uninitialized in this function [-Werror=uninitialized]
I would like to know: why is GCC so easily fooled here? When the code is not in a loop, GCC knows that it is wrong. But put the same code in a simple loop and GCC doesn't understand anymore. This bothers me because we rely quite a lot on the compiler to notify us when we make silly mistakes in C++, yet it fails for a seemingly trivial case.
Bonus trivia:
If you change std::string to int and turn on optimization, GCC will diagnose the error even with the loop.
If you build the broken code with -O3, GCC literally calls the ostream insert function with a null pointer for the string argument. If you thought you were safe from null references if you didn't do any unsafe casting, think again.
I have filed a GCC bug for this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63203 - I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
I'd still like to get a better understanding here of what went wrong and how it may impact the reliability of similar diagnostics.
Unlike Clang, GCC doesn't have logic to detect self-initialized references, so getting a warning here relies on the code for detecting use of uninitialized variables, which is quite temperamental and unreliable (see Better Uninitialized Warnings for discussion).
With an int the compiler can figure out that you write an uninitialized int to the stream, but with a std::string there are apparently too many layers of abstraction between an expression of type std::string and getting the const char* it contains, and GCC fails to detect the problem.
e.g. GCC does give a warning for a simpler example with less code between the declaration and use of the variable, as long as you enable some optimization:
extern "C" int printf(const char*, ...);
struct string {
string() : data(99) { }
int data;
void print() const { printf("%d\n", data); }
};
int main()
{
for (int ii = 0; ii < 1; ++ii)
{
const string& str = str; // !!
str.print();
}
}
d.cc: In function ‘int main()’:
d.cc:6:43: warning: ‘str’ is used uninitialized in this function [-Wuninitialized]
void print() const { printf("%d\n", data); }
^
d.cc:13:19: note: ‘str’ was declared here
const string& str = str; // !!
^
I suspect this kind of missing diagnostic is only likely to affect a handful of diagnostics which rely on heuristics to detect problems. These would be the ones that give a warning of the form "may be used uninitialized" or "may violate strict aliasing rules", and probably the "array subscript is above array bounds" warning. Those warnings are not 100% accurate and "complicated" logic like loops(!) can cause the compiler to give up trying to analyse the code and fail to give a diagnostic.
IMHO the solution would be to add checking for self-initialized references at the point of initialization, and not rely on detecting it is uninitialized later when it gets used.
You claim it's undefined behavior, but when I compile the two cases to assembly, I definitely see the function-scoped variable not being initialized on the stack, and the block-scoped variable getting set to NULL.
That's as much of an answer as you're getting from me. I downloaded the C++ spec to definitively settle this, but fell into a Lovecraftian-type fugue when I gazed upon it, to preserve my fragile sanity...
I strongly suspect the block-scoped case is not actually undefined.

Code compiles in ideone but not with gcc

I wrote the following code:
#include <iostream>
using namespace std;
int main()
{
int v()
return 0;
}
I ran it in ideone, and it compiled successfully. I have the same code in file test1.cpp on my computer, I ran g++ test1.cpp and I got the following error:
./test1.cpp: In function ‘int main()’:
./test1.cpp:7:2: error: a function-definition is not allowed here before ‘return’
Why dose this happen? is this a bug?
I'm using linux mint, gcc version 4.7.
You are missing a semi-colon here:
int v()
^
should be:
int v() ;
which is a function declaration, not clear that was what was intended though. If you want to initialize v then the following would work:
int v(0) ;
or in C++11:
int v{0} ;
This is commonly known as C++'s most vexing parse. When you do something like
int f();
the compiler reads this as a function prototype, declaring a function f that returns an int. If you're using C++11, you should instead do
int f{}; // f initialized to 0
if you're not using C++11, make sure to initialize the variable right away.
You forgot the semicolon after
int v();
Ideone is using gcc 4.8.1 for your code (as you can see in your own link) while you are using 4.7
There are several difference regarding C++ 11 implementation, and apparently it is affected by the line that looks like a function delcaration.

RapidXML compilation error parsing string

I have been having some trouble using RapidXML to parse a string. I receive an error from within Eclipse claiming the parse function does not exist.
make all
Building file: ../search.cpp
Invoking: Cross G++ Compiler
g++ -DDEBUG -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"search.d" -MT"search.d" -o "search.o" "../search.cpp"
../search.cpp: In function ‘void search(CURL*, CURLcode, std::string, std::string)’:
../search.cpp:29:27: error: no matching function for call to ‘rapidxml::xml_document<>::parse(const char*)’
../search.cpp:29:27: note: candidate is:
../rapidxml-1.13/rapidxml.hpp:1381:14: note: template<int Flags> void rapidxml::xml_document::parse(Ch*) [with int Flags = Flags, Ch = char]
make: *** [search.o] Error 1
The following code raises an error:
rapidxml::xml_document<> doc; // This has no errors
doc.parse<0>(data.c_str()); // This line raises the error (data is a string)
For reference here is the online documentation:
http://rapidxml.sourceforge.net/manual.html#namespacerapidxml_1parsing
RapidXML comes as four header files:
rapidxml_iterators.hpp
rapidxml_print.hpp <--contains errors, but build is successful with them
rapidxml_utils.hpp <--contains errors, but build is successful with them
rapidxml.hpp <--linked by program, contains parse function
How do I resolve the error in my code, and do I first need so resolve compiler errors in the headers somehow?
The problem is that the char* returned from calling c_str() on a std::string is actually a const char* which is no good for the parse function (parsing actually changes the string that it parses in rapidXML). This means we need to copy the string before we parse it
xml_document<> doc;
string str; // String you want to parse
char* cstr = new char[str.size() + 1]; // Create char buffer to store string copy
strcpy (cstr, str.c_str()); // Copy string into char buffer
doc.parse<0>(cstr); // Pass the non-const char* to parse()
// Do stuff with parsing
delete [] cstr; // free buffer memory when all is finished
I have not attempted to compile the above so there maybe errors, the point is that c_str() returns a const char* and parse() must take a non-const char*. Hope this helps. As for your headers, I usually get away with only using
rapidxml.hpp
rapidxml_print.hpp
included in my source files. You have no linker issues because RapidXML is header only implementation (which makes it nice in my opinion).
As #Hydranix pointed out in the comments,
doc.parse<0>(&str[0]);
works perfectly well. I parsed some big xml files using it.