Skip to content

Instantly share code, notes, and snippets.

@kulp
Created May 27, 2011 18:28
Show Gist options
  • Save kulp/162ce8447b4f739abaf0 to your computer and use it in GitHub Desktop.
Save kulp/162ce8447b4f739abaf0 to your computer and use it in GitHub Desktop.
Small cpp-based wrapper to provide C entry points for C++ libraries that don't need to be C++
#include "cshim.h"
#if CXX_VISIBLE
#define VISIBILITY extern "C"
#else
#define VISIBILITY static
#endif
#define CXX_SHIM(RetType,CxxFunc,Args,...) \
VISIBILITY RetType CXX_NAME(CxxFunc) (CXX_OBJ c VA_ARGS(__VA_ARGS__)) \
{ \
return ((CXX_CLASS*)c->inner)->CxxFunc Args; \
}
CXX_NAME(SHIM_LIST)(CXX_SHIM)
CXX_OBJ CXX_NAME(create)()
{
CXX_OBJ me = new CXX_STRUCT_TAG();
me->inner = new CXX_CLASS();
#define CXX_ASSIGN(RetType,CxxFunc,Args,...) \
me->CxxFunc = CXX_NAME(CxxFunc);
CXX_NAME(SHIM_LIST)(CXX_ASSIGN)
return me;
}
void CXX_NAME(destroy)(CXX_OBJ *what)
{
delete (CXX_CLASS*)(*what)->inner;
delete *what;
*what = 0;
}
// types
#ifndef CSHIM_HH_
#define CSHIM_HH_
#ifndef CXX_PREFIX
#error "#define CXX_PREFIX to an identifier"
#endif
#ifndef CXX_CLASS
#error "#define CXX_CLASS to a class name"
#endif
#if defined(CXX_INCLUDE_C)
#include CXX_INCLUDE_C
#endif
#if defined(CXX_INCLUDE) && defined(__cplusplus) && !defined(MAKE_HUMAN_READABLE_CSHIM_H_)
#include CXX_INCLUDE
#endif
#define VA_ARGS(...) , ##__VA_ARGS__
#define CAT3_(A,B,C) \
A ## B ## C
#define CAT3(A,B,C) \
CAT3_(A,B,C)
#define CXX_NAME(Stem) \
CAT3(CXX_PREFIX,_,Stem)
#define CXX_OBJ \
CXX_PREFIX
#define CXX_STRUCT_TAG \
CXX_NAME()
// wraps preprocessor directives to hide them from first-level interpretation
#define EMIT(...) __VA_ARGS__
#if __cplusplus && !defined(MAKE_HUMAN_READABLE_CSHIM_H_)
extern "C" {
#endif
// *_human.h is created from this file through a makefile rule. The _human file
// is the one intended to be included from a user program, since it is much more
// readable than this file.
#if MAKE_HUMAN_READABLE_CSHIM_H_
EMIT(#ifndef CXX_NAME(CSHIM_H_))
EMIT(#define CXX_NAME(CSHIM_H_))
EMIT(#include CXX_INCLUDE_C)
#endif
typedef struct CXX_STRUCT_TAG *CXX_OBJ;
#if CXX_VISIBLE
#define CXX_FDEF(RetType,CxxFunc,Args,...) \
RetType CxxFunc(CXX_OBJ me VA_ARGS(__VA_ARGS__));
CXX_NAME(SHIM_LIST)(CXX_FDEF)
#endif
struct CXX_STRUCT_TAG {
void *inner;
#define CXX_DEF(RetType,CxxFunc,Args,...) \
RetType (*CxxFunc)(CXX_OBJ me VA_ARGS(__VA_ARGS__));
CXX_NAME(SHIM_LIST)(CXX_DEF)
};
CXX_OBJ CXX_NAME(create)(void);
void CXX_NAME(destroy)(CXX_OBJ *what);
#if __cplusplus && !defined(MAKE_HUMAN_READABLE_CSHIM_H_)
};
#endif
#if MAKE_HUMAN_READABLE_CSHIM_H_
EMIT(#endif)
#endif
#endif
CPPFLAGS += $(INCLUDE:%=-I%) $(DEFINES:%=-D%)
CXXFLAGS += -Wall -Wextra -Wno-unused
CC = gcc
LD = gcc
CFILES += $(wildcard *.c)
CXXFILES += $(wildcard *.cc)
CPP = g++ -x c++ -E -P
#DEFINES += CXX_VISIBLE=1
CFLAGS += -g
CXXFLAGS += -g
CXXFLAGS += -O3
#CXXFLAGS += -save-temps
CFLAGS += -save-temps
.DEFAULT_GOAL = all
all: $(TARGETS) libcshim_$(CXX_CLASS).so
CLEANFILES += $(TARGETS)
$(TARGETS): CFLAGS += -std=c99
$(TARGETS): LDFLAGS += $(CXX_LDFLAGS)
$(TARGETS): LDLIBS += -lcshim_$(CXX_CLASS) $(CXX_LDLIBS)
$(TARGETS): | libcshim_$(CXX_CLASS).so
# TODO try to restruct the targets ? rename .so ?
DEFINES += 'CXX_PREFIX=$(CXX_PREFIX)'
DEFINES += 'CXX_CLASS=$(CXX_CLASS)'
DEFINES += 'CXX_INCLUDE="$(CXX_INCLUDE)"'
DEFINES += 'CXX_INCLUDE_C="$(CXX_INCLUDE_C)"'
DEFINES += 'CXX_INCLUDE_SHIM="$(CXX_INCLUDE_SHIM)"'
CLEANFILES += *_cshim.h # TODO unsafe
# requires make rules to check header dependencies, or to build cshim_human.h
# independently
$(CXX_CLASS)_cshim.h: cshim.h $(CXX_INCLUDE)
$(CPP) $(CPPFLAGS) -DMAKE_HUMAN_READABLE_CSHIM_H_ $< | indent > $@.$$$$ && mv $@.$$$$ $@
cshim_$(CXX_CLASS),fPIC.o: CXXFLAGS += -fPIC
cshim_$(CXX_CLASS),fPIC.o: $(CXX_CLASS).cc
$(COMPILE.cc) -o $@ $^
%,fPIC.o: CXXFLAGS += -fPIC
%,fPIC.o: %.cc
$(COMPILE.cc) -o $@ $^
# TODO there are some dependencies missing on header files ; can get binary
# incompatibilities
CLEANFILES += cshim_$(CXX_CLASS),fPIC.o libcshim_$(CXX_CLASS).so
libcshim_$(CXX_CLASS).so: cshim,fPIC.o cshim_$(CXX_CLASS),fPIC.o
$(LINK.cc) -shared -o $@ $^
clean:
$(RM) $(CLEANFILES) *.o *.ii *.i *.s
#CLEANFILES += *.d
#ifneq ($(MAKECMDGOALS),clean)
#-include $(CFILES:.c=.d)
#-include $(CXXFILES:.cc=.d)
#endif
#
#%.d: %.c
# @set -e; rm -f $@; \
# $(CC) $(CPPFLAGS) -MM -MG -MF $@.$$$$ $<; \
# sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
# rm -f $@.$$$$
#
#%.d: %.cc
# @set -e; rm -f $@; \
# $(CC) $(CPPFLAGS) -MM -MG -MF $@.$$$$ $<; \
# sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
# rm -f $@.$$$$
# the name of the class being wrapped
export CXX_CLASS = Test
# the prefix for C function names created
export CXX_PREFIX = TestWrap
# the C++ class declaration header
export CXX_INCLUDE = Test.h
# the header that contains the _SHIM decl
export CXX_INCLUDE_C = TestWrap.h
.PHONY: all clean
all: libcshim_Test.so
$(MAKECMDGOALS) clean:
$(MAKE) -f Makefile.cshim $@
libcshim_Test.so Test_cshim.h:
$(MAKE) -f Makefile.cshim $@
all: testuser
testuser: libcshim_Test.so
libcshim_Test.so: Test_cshim.h
#include "Test.h"
#include <iostream>
#include <cstdlib>
int Test::squawk(int a, char b) {
std::cout << "squawking " << a + b << std::endl;
return a + b;
}
void Test::die() {
abort();
}
#ifndef TEST_H_
#define TEST_H_
class Test {
public:
int squawk(int a, char b);
void die();
};
#endif
#include "Test_cshim.h"
int main(void)
{
TestWrap foo = TestWrap_create();
foo->squawk(foo, 5, 4);
foo->die(foo);
TestWrap_destroy(&foo);
return 0;
}
#define TestWrap_SHIM_LIST(_) \
_(int,squawk,(a,b),int a, char b) \
_(void,die,()) \
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment