Here's the use case:
I have a .cpp
file which has functions implemented in it. For sake of example say it has the following:
[main.cpp]
#include <iostream>
int foo(int);
int foo(int a) {
return a * a;
}
int main() {
for (int i = 0; i < 5; i += 1) {
std::cout << foo(i) << std::endl;
}
return 0;
}
I want to perform some amount of automated testing on the function foo
in this file but would need to replace out the main()
function to do my testing. Preferably I'd like to have a separate file like this that I could link in over top of that one:
[mymain.cpp]
#include <iostream>
#include <cassert>
extern int foo(int);
int main() {
assert(foo(1) == 1);
assert(foo(2) == 4);
assert(foo(0) == 0);
assert(foo(-2) == 4);
return 0;
}
I'd like (if at all possible) to avoid changing the original .cpp
file in order to do this -- though this would be my approach if this is not possible:
"(\s)main\s*\("
==> "\1__oldmain\("
The environment I am targeting is a linux environment with g++.
In symbol resolution, weak defined symbols are silently overridden by any global definition of the same name. Another form of simple symbol resolution, interposition, occurs between relocatable objects and shared objects, or between multiple shared objects.
Linking object files to produce an executable The linker combines the contents of one or more object files with selected parts of any required object libraries to produce executable images, partially linked object files, or shared object files.
Header files are not compiled directly, Instead, header files are included into other source files via #include .
Each C++ source file needs to be compiled into an object file. The object files resulting from the compilation of multiple source files are then linked into an executable, a shared library, or a static library (the last of these being just an archive of object files).
I hate answering my own question, but here's a solution I ended up finding deep in the man page of g++
, I've tested it and it works to what I would want it to...
g++
has the -D
flag which allows you to define macros when compiling object files. I know you are thinking "ugh macros" but hear me out... You can use the macro definition to effectively rename a symbol. In my case, I can run the following command to generate an object file of my students code without their main file: g++ -D main=__students_main__ main.cpp -c -o main.nomain.o
.
This creates an object file with their int main
defined as int __students_main__
. Now this isn't necessarily callable directly as they could have defined main as int main(void)
or with the various combinations of argc
and argv
, but it allows me to effectively compile out their function.
The final compile looks like this:
g++ -c -D main=__students_main__ main.cpp -o main.nomain.o
g++ -c mymain.cpp -o mymain.o
g++ main.nomain.o mymain.o -o mymainstudentsfoo.out
For my purposes, I wanted to create a Makefile that would accomplish this automagically (ish) and I feel that is relevant to this discussion so I'll post what I came up with:
HDIR=./ # Not relevant to question, but we have headers in a separate directory
CC=g++
CFLAGS=-I $(HDIR)
NOMAIN=-D main=__student_main__ # The main renaming magic
.SECONDARY: # I forget exactly what this does, I seem to remember it is a hack to prevent deletion of .o files
cpp = $(wildcard *.cpp)
obj = $(patsubst %.cpp,%.o,$(cpp))
objnomain = $(patsubst %.cpp,%.nomain.o,$(cpp))
all: $(obj) $(objnomain)
clean:
rm -f *.o *.out
%.nomain.o: %.cpp
$(CC) $(CFLAGS) $(NOMAIN) -c $^ -o $@
%.o: %.cpp
$(CC) $(CFLAGS) -c $^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With