CUDA as a Cross-Platform Scripting Language
Combining the previous dynamic comple/link and protobuf examples yields the powerful "click-together" framework I mentioned earlier.
The following is the complete source for dynFunc.cc, which combines the streaming of protobuf messages and the dynamic loading of plugins. Windows users will need to precompile the DLLs, while the Linux version dynamically compiles the code at runtime. Production code needs to ensure there are no name conflicts. One possibility is to add the PID (Process ID) to the plugin name.
For flexibility, all methods have the ability to create new messages that can be passed on to other applications in the click-together framework. The plugin method is responsible for creating a char array containing the serialized message. The method returns a pointer to the character array that contains the message. The plugin method must also modify type and size to reflect the new message contents. Returning NULL implies no message is to be written. To simplify memory management, the dynFree() method is used to free the region of memory (for example, C source code would use malloc()/free() and C++ source code would use new/delete).
//Rob Farber
#ifdef _WIN32
#define DYN_INCLUDE "../protobuf_examples/protobuf-2.4.1/vsprojects/include"
#include <cstdint>
extern __declspec(dllimport) char* init(const char*, const char*);
extern __declspec(dllimport) char* func(const char*, const char*,
uint32_t*, uint32_t*, char*);
extern __declspec(dllimport) char* fini(const char*, const char*);
extern __declspec(dllimport) void dynFree(char*);
#else
#include <cstdlib>
#include <sys/types.h>
#include <dlfcn.h>
#endif
#include <string>
#include <iostream>
#include "packetheader.h"
using namespace std;
#ifdef _WIN32
HINSTANCE lib_handle;
#else
void *lib_handle;
#endif
typedef char* (*initFini_t)(const char*, const char*, uint32_t*, uint32_t*);
typedef char* (*func_t)(const char*, const char*, uint32_t*, uint32_t*, char*);
typedef void (*dynFree_t)(char*);
int main(int argc, char **argv)
{
if(argc < 2) {
cerr << "Use: sourcefilename" << endl;
return -1;
}
string base_filename(argv[1]);
base_filename = base_filename.substr(0,base_filename.find_last_of("."));
// build the shared object or dll
#ifdef _WIN32
string buildCommand("nvcc ");
buildCommand += string(" -Xcompiler \"/MD\" -I ")
+ string(DYN_INCLUDE) + string(" ")
+ string(argv[1]) + string(" tutorial.pb.cc ")
+ string(" ")
+ string(" -Xlinker \"/DLL /OUT:") + base_filename + string(".dll \"");
// See article: Windows only links to pre-made DLLs
#else
string buildCommand("nvcc -Xcompiler \"-fPIC -shared\" ");
buildCommand += string(argv[1])
+ string(" -o ") + base_filename + string(".so ");
cerr << "Compiling with \"" << buildCommand << "\"" << endl;
if(system(buildCommand.c_str())) {
cerr << "compile command failed!" << endl;
cerr << "Build command " << buildCommand << endl;
return -1;
}
#endif
// load the library ------------
string nameOfLibToLoad("./");
nameOfLibToLoad += base_filename;
#ifdef _WIN32
nameOfLibToLoad += ".dll";
lib_handle = LoadLibrary(TEXT(nameOfLibToLoad.c_str()));
if (!lib_handle) {
cerr << "Cannot load library: " << TEXT(nameOfLibToLoad.c_str()) << endl;
return -1;
}
#else
nameOfLibToLoad += ".so";
lib_handle = dlopen(nameOfLibToLoad.c_str(), RTLD_LAZY);
if (!lib_handle) {
cerr << "Cannot load library: " << dlerror() << endl;
return -1;
}
#endif
// load the symbols ------------
initFini_t dynamicInit= NULL;
func_t dynamicFunc= NULL;
initFini_t dynamicFini= NULL;
dynFree_t dynamicFree= NULL;
#ifdef _WIN32
dynamicInit = (initFini_t) GetProcAddress(lib_handle, "init");
if (!dynamicInit) {cerr << "sym load error on init" << endl; return -1;}
dynamicFunc = (func_t) GetProcAddress(lib_handle, "func");
if (!dynamicFunc) {cerr << "sym load error on func" << endl; return -1;}
dynamicFini = (initFini_t) GetProcAddress(lib_handle, "fini");
if (!dynamicInit) {cerr << "sym load error on fini" << endl; return -1;}
dynamicFree = (dynFree_t) GetProcAddress(lib_handle, "dynFree");
if (!dynamicFree) {cerr << "sym load error on dynFree" << endl; return -1;}
#else
// reset errors
dlerror();
// load the function pointers
dynamicFunc= (func_t) dlsym(lib_handle, "func");
const char* dlsym_error = dlerror();
if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
dynamicInit= (initFini_t) dlsym(lib_handle, "init");
dlsym_error = dlerror();
if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
dynamicFini= (initFini_t) dlsym(lib_handle, "fini");
dlsym_error = dlerror();
if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
dlsym_error = dlerror();
if (dlsym_error) { cerr << "sym load: " << dlsym_error << endl; return -1;}
dynamicFree= (dynFree_t) dlsym(lib_handle, "dynFree");
#endif
//
// work with protobufs
//
//enable C++ binary cin and cout
if (!setPacket_binaryIO()) {
cerr << "Cannot set binary mode for cin and cout!" << endl;
return -1;
}
uint32_t size, type;
char *retBlob;
// handle initialization and put information on output stream when told
if( (retBlob=(*dynamicInit)(argv[0], base_filename.c_str(),&size, &type)) ) {
writePacketHdr(size, type, &std::cout);
cout.write(retBlob, size);
(dynamicFree)(retBlob);
}
// read stream from cin and put information on output stream when told
while(readPacketHdr(&size, &type, &std::cin)) {
char *blob = new char[size];
cin.read(blob, size);
retBlob =(*dynamicFunc)(argv[0], base_filename.c_str(), &size, &type, blob);
if(retBlob) {
writePacketHdr(size, type, &std::cout);
cout.write(retBlob, size);
// optimization: if retBlob == blob then allocated was by this program
if(retBlob != blob) (dynamicFree)(retBlob);
}
delete [] blob;
}
// handle finalization (fini) and put information on output stream when told
if( retBlob = (*dynamicFini)(argv[0], base_filename.c_str(),&size, &type) ) {
writePacketHdr(size, type, &std::cout);
cout.write(retBlob, size);
(dynamicFree)(retBlob);
}
// unload the library -----------------------------------------------
#ifdef _WIN32
FreeLibrary(lib_handle);
#else
dlclose(lib_handle);
#endif
return 0;
}
The source for passthrough.cc is included below. This plugin simply passes all messages along to the next application in the pipeline. It is important that Windows users note the use of #pragma comment() to inform the linker about needed libraries. In this way, the protobuf method can be linked with the plugin. Linux users will note the nvcc command includes use of the –rdynamic option, which tells the linker to check the executable for any unresolved symbols.
//passthrough.cc (Rob Farber)
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#include "tutorial.pb.h"
using namespace std;
#ifdef _WIN32
// tell the linker about needed libraries
#pragma comment(lib,"libprotobuf")
#endif
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
char* init(const char* progname, const char* sourcename,
uint32_t *size, uint32_t *type) {
return(NULL);
}
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
char* func(const char* progname, const char* sourcename,
uint32_t *size, uint32_t *type, char *blob)
{
return(blob); //Note: this is a special case that will not invoke dynFree
}
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
char* fini(const char* progname, const char* sourcename,
uint32_t *size, uint32_t *type) {
return(NULL);
}
extern "C"
#ifdef _WIN32
__declspec(dllexport)
#endif
void dynFree(char* pt) {
cerr << "dynFree" << endl;
if(pt) delete [] pt;
}



