C++
This program works by opening its own source file, extracting the desired function, writing it to a new source file (which prints the result), compiling it, then executing it and reading its output.
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <string>
using namespace std;
//=== here's all our "callable" functions ===
int add (int a, int b) {
return a + b;
}
int indexof (const char *str, char c) {
for (int n = 0; str[n]; ++ n) {
if (str[n] == c)
return n;
}
return -1;
}
const char * addsuffix (int n) {
static char buffer[20];
switch (n % 10) {
case 1: sprintf(buffer, "%dst", n); break;
case 2: sprintf(buffer, "%dnd", n); break;
default: sprintf(buffer, "%dth", n); break;
}
return buffer;
}
//=== end of "callable" functions ===
int count (const char *s, int c) {
int n = 0;
while (*s)
n += (*(s ++) == c);
return n;
}
template <typename ReturnType>
ReturnType icall (const char *fn, const char *p) {
// generate output
ifstream me(__FILE__);
ofstream out("temp~.cpp");
char line[1000];
int infunc = 0;
while (!me.eof()) {
me.getline(line, sizeof(line));
if (!strncmp(line, "#include", 8))
out << line << endl;
else if (!strncmp(line, "using", 5))
out << line << endl;
else {
if (!infunc) {
char *fname = strstr(line, fn);
if (fname) {
infunc += 1;
*fname = 0;
out << line << "oneTrickPony" << (fname + strlen(fn)) << endl;
*fname = ' ';
}
} else {
out << line << endl;
}
if (infunc)
infunc += count(line, '{') - count(line, '}');
if (strchr(line, '}') && infunc == 1)
break;
}
}
out << "int main () { cout << oneTrickPony(" << p << "); }" << endl;
out.close();
// compile output
system("g++ temp~.cpp -o temp~.exe"); // exe suffix ok on linux
// execute output
FILE *res = popen("temp~.exe", "r");
fgets(line, sizeof(line), res);
pclose(res);
// parse output
ReturnType rval;
stringstream rss(line);
rss >> rval;
return rval;
}
#define call(rt,fn,p...) icall<rt>(fn, #p)
int main () {
// call functions and store results
int resulta = call(int, "add", 14, 99);
int resultn = call(int, "indexof", "abcdefg", 'e');
string results = call(string, "addsuffix", 1);
// print results
cout << resulta << endl
<< resultn << endl
<< results << endl;
}
The icall
function is responsible for doing all the dirty work; the template type is the return type and is used for parsing the output into a usable value. The call
macro exists to allow us to write a variable number of function parameters to the generated source file. The calls are made in main
:
// call functions and store results
int resulta = call(int, "add", 14, 99);
int resultn = call(int, "indexof", "abcdefg", 'e');
string results = call(string, "addsuffix", 1);
Requires gcc. Tested on MinGW.
Supports:
- Any return types supported by the
istream
>>
operator.
- Runtime function names.
- Any number of parameters, of any type.
Does not support:
- Non-constant function parameters. This is a tradeoff for number 3 and also for brevity. I could add support for this by dropping the macro and using more templates, but I didn't want to add too much code to distract from the general idea.
- Functions with
void
return types. A specialized icall
that does not parse the returned value would allow this. Again, didn't go too crazy.
A little messy but gets the job done. It's pretty good at parsing itself but will fail under certain conditions (e.g. if a curly brace appears in a string, or the function name appears before the function in another context... I didn't really want to implement a full-on C++ parser...).
Here is an example generated file for a call to indexof
:
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <string>
using namespace std;
int oneTrickPony (const char *str, char c) {
for (int n = 0; str[n]; ++ n) {
if (str[n] == c)
return n;
}
return -1;
}
int main () { cout << oneTrickPony("abcdefg", 'e'); }