Undefined Symbol When Mixing C and C++ Code - c++

I'm just getting started using nauty, which is written in C. Nauty comes with a program called geng that produces a file of graphs, but it's possible to call it from your own program, and work with the graphs one at a time. A sample C program is provided, and I'm trying to convert it to C++. The comments at the top of the program describe the general approach. I've listed both my program and geng.c, the source code from nauty, in my target.
Here's my code
/* This is a sample of how to call geng as a procedure rather than
* running it as a separate process. The basic idea is to construct
* an argument list for geng's main() function. At compile time,
* assign a name to the macros OUTPROC and GENG_MAIN. A typical
* Unix-style compilation command would be:
gcc -o callgeng -O3 -DMAXN=32 -DOUTPROC=myoutproc -DGENG_MAIN=geng_main \
callgeng.c geng.c nauty.a
*/
extern "C" {
#include "gtools.h"
}
static unsigned long counter;
extern "C" void
OUTPROC(FILE *outfile, graph *g, int n)
{
/* This will be called for each graph. */
++counter;
}
int GENG_MAIN(int geng_argc, char* geng_argv[]);
int
main(int argc, char *argv[])
{
int geng_argc;
char *geng_argv[6];
// Set up geng argument list. The 0-th argument is the command name.
// There must be a NULL at the end. This example is for trees
// of order 16.
char argv0[] = "geng";
char argv1[] = "-q";
char argv2[] = "-cbf";
char argv3[] = "16";
char argv4[] = "15";
geng_argv[0] = argv0;
geng_argv[1] = argv1;
geng_argv[2] = argv2;
geng_argv[3] = argv3;
geng_argv[4] = argv4;
geng_argv[5] = NULL;
geng_argc = 5;
counter = 0;
GENG_MAIN(geng_argc,geng_argv);
printf("Number of graphs = %lu.\n",counter);
return 0;
}
The program geng.c contains the lines:
#ifdef GENG_MAIN
int
GENG_MAIN(int argc, char *argv[])
#else
int
main(int argc, char *argv[])
#endif
When I try to build the project, it fails with a linker error:
Undefined symbols for architecture x86_64: "_geng_main", referenced
from:
_main in my_callgeng.o ld: symbol(s) not found for architecture x86_64
I tried compiling from the command line and it works fine.
>gcc -o callgeng -O3 -DMAXN=32 -DOUTPROC=myoutproc -DGENG_MAIN=geng_main -I/Users/saul/nauty26r7 my_callgeng.cpp ~/nauty26r7/geng.c ~/nauty26r7/nauty.a
>./callgen
Number of graphs = 19320.
So, it looks like an Xcode problem.

This was an Xcode error. I didn't realize that I had to put the compiler flags on each each line under BuildPhases/Compile Sources, so I only had "-DGENG_MAIN=geng_main" for my code, not for geng.c. Therefore the substitution didn't take place when geng.c was compiled, and it must have compiled _main, not _geng_main.

In your code (not geng.c), you need to add the extern "C" in front of
int
GENG_MAIN(int argc, char *argv[])
so that the compiler knows that this function (from geng.c) must use C linkage within your C++ instead of the default C++ linkage for that function (what you get when you don't specify in a file full of C++ code).
Basically, the C++ file uses C++ linkage for functions by default, and the C code uses C linkage for functions, so when you want the C code to be able to call something from the C++ code (your GENG_MAIN) you must specify in the C++ code that that particular function must be compiled with C linkage even though it is internally C++.

Related

When mixing C and C++ code, does main() need to be in the C++ part?

I have a C program that I need to interface with a C++ library (ROS). Normally, it's not too difficult to interface C code with C++ code with a wrapper using extern "C" and using the C++ compiler to link, but I've never had to do it where main was in the C portion.
The C++ FAQ indicates that this is a bad thing:
Here are some high points (though some compiler-vendors might not require all these; check with your compiler-vendor’s documentation):
You must use your C++ compiler when compiling main() (e.g., for static initialization)
But I see another source saying it should be okay these days:
At one time, most C++ compilers required that function main be compiled by the C++ compiler. That requirement is not common today, ...
Why would it matter whether main is in the C portion or the C++ portion? How much trouble would I be in if I try to link code where it's in the C portion using common linkers today (mainly GCC's and Clang's)?
One easy way to work around this is to rename your C main() and call it from a new C++ main()
As in:
// in ypur current C main module
int my_c_main(int argc, char* argv[]) /* renamed, was main() */
{
/* ... */
]
// in a c++ module...
int main(int argc, char* argv[])
{
extern "C" int my_c_main(int argc, char* argv[]);
// if your c main() requires environment variables passed in envp,
// You can allocate space for strings and an array here and pass
// the environment variables you'll need, as the third parameter
// to my_c_main(), or pass environ, if your system has
// it defined in unistd.h
return my_c_main(argc, argv);
}

Building a temporary `main()` function in c++

I'm just learning c++. I have a main.cpp unit which has a lot of stuff in it already, and I just want to build a quick little testMain.cpp unit that will test a couple of things.
Basically I want to trick the compiler (xCode) into ignoring the real main function for a minute. I could
Rename the main() function inside main.cpp to mmain() temporarily.
Remove the reference to main.cpp in my project temporarily.
Comment out the main() method in main.cpp temporarily.
All these seem pretty clunky. There has to be an easier way. I suspect this is a common thing people do. How do you do it?
Another solution would be to separate the code into multiple files, have most of the logic in one file, have the real main in another and the test main in a third, you compile and link either the first and second or first and third files but never all three together.
Your option 2 is the most common strategie, and from my understanding also the cleanest.
After all, your test application will most likely not share the same command line interface, and that is usually about the only thing which should (of at all) be located in the main function or file.
If your main.cpp contains significantly more than just the entry point, you should immediately start thinking about how to distribute that logic into the modules you already have.
Use a macro.
Option 1: Use a macro to include/exclude entire files:
main.cpp:
#ifdef USE_REAL_MAIN
int main(int argc, char* argv[]) {
...
}
#endif
testMain.cpp
#ifdef USE_TEST_MAIN
int main(int argc, char* argv[]) {
...
}
#ENDIF
build file:
gcc -DUSE_REAL_MAIN
gcc -DUSE_TEST_MAIN
Option 2: Use a command-line macro to rename main:
main.cpp:
int realMain(int argc, char* argv[]) {
...
}
testMain.cpp
int testMain(int argc, char* argv[]) {
...
}
build file:
gcc -DrealMain=main
gcc -DtestMain=main
Note this is probably the least attractive option because it breaks the convention of macros having UPPER_CASE names and means the real entrypoint of your program is non-obvious to someone who hasn't seen the build script. It also means the program simply won't compile (as there's no main function) without your custom build script either.
Option 3: Have a new common main with the #ifdef directives instead:
main.cpp
#include "realMain.h"
#include "testMain.h"
int main(int argc, char* argv[]) {
#ifdef USE_TEST_MAIN
return testMain( argc, argv );
#else
return realMain( argc, argv );
#endif
}
build file:
gcc -DUSE_REAL_MAIN
gcc -DUSE_TEST_MAIN
I think this is my preferred option because it's almost self-documenting and makes it clear to another programmer how to get it to work without needing your custom build script.

Objective-C variables read as null in C++

I'm writing a cross-platform application and need to pass across argc and argv from Objective-C in Xcode to my generic argument handler class in C++. I have a global pointer to this handler class which I set with a new command, but because I can't do new in Objective-C I'm trying the following:
I have a header file called MacCommandLineArgs.h which contains only the following:
static int cmdlArgc = 0;
static const char** cmdlArgv = NULL;
I then set these from within main.m:
int main(int argc, char *argv[])
{
cmdlArgc = argc;
cmdlArgv = (const char**)argv;
return NSApplicationMain(argc, (const char **)argv);
}
Once the Objective-C++ section of the application has been entered, I try to read back these global variables in order to pass them to the pure-C++ class:
int argc = cmdlArgc;
const char** argv = cmdlArgv;
globalCommandLineArgs = new CCommandLineArgs(argc, argv);
When stepping through with the debugger, cmdlArgc and cmdlArgv show up as valid data but argc and argv are still 0 and NULL after the assignment. What am I doing wrong here?
You don't show how cmdlArgc and cmdlArgv are declared; it's surely in a header file, but what does it look like?.
Nothing outside of main.m will have access to those variables as they have been defined static, which leads me to wonder why you aren't getting linker errors. I have concluded that the declaration of the of cmdlArgc and cmdArgv is something like this:
int cmdlArgc;
const char** cmdlArgv;
rather than this:
extern int cmdlArgc;
extern const char** cmdlArgv;
So every implementation file that includes the header will get their own copy, which is why it's 0/NULL.
The solution is to drop the use of static in main.m and start using extern in the header.
However it's still an ugly pattern and I would think the very best solution is to rename main.m to main.mm and initialise CCommandLineArgs in main(), which is both clean and simple.

What are the major parts of a Tizen application's entry file?

Because Tizen is still not so popular, I couldn't find Tizen application entry file explanation. Could anyone, based on the following example code, explain particular parts of Tizen entry file (Main function return value, #ifdef, args...)?
#include <new>
#include "MultipointTouch.h"
using namespace Tizen::Base;
using namespace Tizen::Base::Collection;
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
_EXPORT_ int OspMain(int argc, char* pArgv[]);
/**
* The entry function of Tizen C++ application called by the operating system.
*/
int
OspMain(int argc, char* pArgv[])
{
AppLog("Application started.");
ArrayList args;
args.Construct();
for (int i = 0; i < argc; i++)
{
args.Add(*(new (std::nothrow) String(pArgv[i])));
}
result r = Tizen::App::Application::Execute(MultipointTouch::CreateInstance, &args);
TryLog(r == E_SUCCESS, "[%s] Application execution failed", GetErrorMessage(r));
args.RemoveAll(true);
AppLog("Application finished.");
return static_cast<int>(r);
}
#ifdef __cplusplus
}
#endif // __cplusplus
#ifdef __cplusplus
extern "C
Not Tizen-specific. What it does is to "[make] a function-name in C++ have 'C' linkage (compiler does not mangle the name) so that client C code can link to (i.e use) your function using a 'C' compatible header file that contains just the declaration of your function." (source).
int OspMain(int argc, char* pArgv[])
OspMain is simply the entry point for a Tizen native application (i.e. the first function in your app that the OS calls when your app starts), much like main or WinMain in other OSs/frameworks.
args
The App Execute method expects the arguments as a list of Strings. So the OspMain function takes care of building that list prior to calling the Execute method; a String is created from each char* in argv, and those Strings are placed in an ArrayList (which is an implementation of the IList interface).
return value
The return type of OspMain is int, but the result code it receives from Execute is of type result, so it casts the result to an int. There are plenty of questions on the topic of C++ casts if you want to read more about them.
In the end, I think there are very few instances where you'd have to care about the Entry file as an app developer. It's automatically created for you by the IDE and not something you'd typically change.

C++ undefined reference to defined function

I cannot figure out why this is not working. I will put up all three of my files and possibly someone can tell me why it is throwing this error. I am using g++ to compile the program.
Program:
#include <iostream>
#include "h8.h"
using namespace std;
int main()
{
char sentence[MAX_SENTENCE_LENGTH];
char writeTo[] = "output.txt";
int distanceTo,likePosition, length, numWords;
cout << "ENTER A SENTENCE! ";
cin.getline(sentence, 299);
length = strlen(sentence);
numWords = wordCount(sentence, length);
for(int x = 0; x < 3; ++x)
{
likePosition = likePos(numWords);
distanceTo = lengthTo(sentence, likePosition, length);
insertLike(sentence, distanceTo, length, writeTo);
}
return 0;
}
Function file:
void insertLike(const char sentence[], const int lengthTo, const int length, char writeTo[])
{
char part1[MAX_SENTENCE_LENGTH], part2[MAX_SENTENCE_LENGTH];
char like[] = " like ";
for(int y = 0; y < lengthTo; ++y)
part1[y] = sentence[y];
for(int z = lengthTo+1; z < length - lengthTo; ++z)
part2[z] = sentence[z];
strcat(part1, like);
strcat(part1, part2);
writeToFile(sentence, writeTo);
return;
}
Header file:
void insertLike(const char sentence[], const int lengthTo, const int length, const char writeTo[]);
The error exactly is:
undefined reference to 'insertLike(char const*, int, int, char const*)'
collect2: ld returned 1 exit status
The declaration and definition of insertLike are different
In your header file:
void insertLike(const char sentence[], const int lengthTo, const int length, const char writeTo[]);
In your 'function file':
void insertLike(const char sentence[], const int lengthTo, const int length,char writeTo[]);
C++ allows function overloading, where you can have multiple functions/methods with the same name, as long as they have different arguments. The argument types are part of the function's signature.
In this case, insertLike which takes const char* as its fourth parameter and insertLike which takes char * as its fourth parameter are different functions.
Though previous posters covered your particular error, you can get 'Undefined reference' linker errors when attempting to compile C code with g++, if you don't tell the compiler to use C linkage.
For example you should do this in your C header files:
extern "C" {
...
void myfunc(int param);
...
}
To make 'myfunc' available in C++ programs.
If you still also want to use this from C, wrap the extern "C" { and } in #ifdef __cplusplus preprocessor conditionals, like
#ifdef __cplusplus
extern "C" {
#endif
This way, the extern block will just be “skipped” when using a C compiler.
You need to compile and link all your source files together:
g++ main.c function_file.c
This could also happen if you are using CMake. If you have created a new class and you want to instantiate it, at the constructor call you will receive this error -even when the header and the cpp files are correct- if you have not modified CMakeLists.txt accordingly.
With CMake, every time you create a new class, before using it the header, the cpp files and any other compilable files (like Qt ui files) must be added to CMakeLists.txt and then re-run cmake . where CMakeLists.txt is stored.
For example, in this CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8.11)
project(yourProject)
file(GLOB ImageFeatureDetector_SRC *.h *.cpp)
### Add your new files here ###
add_executable(yourProject YourNewClass.h YourNewClass.cpp otherNewFile.ui})
target_link_libraries(imagefeaturedetector ${SomeLibs})
If you are using the command file(GLOB yourProject_SRC *.h *.cpp) then you just need to re-run cmake . without modifying CMakeLists.txt.
If you are including a library which depends on another library, then the order of inclusion is also important:
g++ -o MyApp MyMain.o -lMyLib1 -lMyLib2
In this case, it is okay if MyLib1 depends on MyLib2.
However, if there reverse is true, you will get undefined references.
As Paul said, this can be a linker complaint, rather than a compiler error. If you read your build output/logs carefully (may need to look in a separate IDE window to see the full details) you can dell if the problem is from the compiler (needs to be fixed in code) or from the linker (and need to be fixed in the make/cmake/project level to include a missing lib).