Report: writing a C extension dll on Cygwin, linking to cpp code
От | Brian K Boonstra |
---|---|
Тема | Report: writing a C extension dll on Cygwin, linking to cpp code |
Дата | |
Msg-id | 4E89C003-CCA4-11D8-915E-00039319807C@boonstra.org обсуждение исходный текст |
Список | pgsql-cygwin |
Hi there I recently found it a bit of a pain in the neck to figure out all the bits for making some QuantLib capabilities available to PostgreSQL running under Cygwin. This post is to document my progress so far in the interest of saving somebody else a little time. Some details might be slightly useful to users of OSX or Linux. Background: QuantLib is a library of financial objects written in C++. It has a Microsoft VC6/7 port, but also compiles under Cygwin. I wanted a proof-of-concept, embedding e.g. the vanilla option pricer into Postgresql as a C extension. There appears to be no documentation on how to write C extensions using Cygwin; what I have learned is culled from various mailing list posts. Overview: (1) Get some kind of C extension to compile and run under Cygwin (the example in the pg docs works fine). (2) Write some C++ wrapper code into a cpp file. (3) Figure out the link step with both C and C++ object files. Exposition: Since Windows expects a DLL rather than a .so, the first thing you have to know is that you are going to end up making a DLL. To do that in Cygwin, use something like this: cc -c mycfile.c -o mycfile.o dlltool --export-all --output-def mylib.def mycfile.o; dllwrap mylib.dll --dllname mylib.dll --def mylib.def mycfile.o -L/lib -lpostgres cp mylib.dll /usr/lib/postgresql/ Try to load the function using fairly ordinary commands: CREATE FUNCTION concat_text(text, text) RETURNS text AS 'mylib', 'concat_text' LANGUAGE C STRICT; where we note that the DLL suffix is left off. Once you have that working, you can write the cpp wrapper. Maybe it looks a little like this: extern "C" { double wrap_foo_do_bar(double x) { Foo aFoo(x); return aFoo.bar(); } } and write the C extension wrapper into your C file like this: PG_FUNCTION_INFO_V1(foobar); Datum f(PG_FUNCTION_ARGS) { float8 arg = PG_GETARG_FLOAT8(0); PG_RETURN_FLOAT8( wrap_foo_do_bar(arg) ); } Now you have to get everything to compile and link properly. You can compile the C file as before, but beware makefiles for the C++ file, since that one might use $CPP, which uses an unwanted -E argument. In addition, you need a C++ aware linker in your dllwrap command. You basically want cc -c mycfile.c -o mycfile.o g++ -c mycppfile.cpp -o mycppfile.o dlltool --export-all --output-def mylib.def mycfile.o mycppfile.o; dllwrap mylib.dll --driver-name g++ --dllname mylib.dll --def mylib.def mycfile.o -L/lib -L. -lpostgres -lmycpplib cp mylib.dll /usr/lib/postgresql/ Worked Example: Here is a makefile, a SQL command, a C file, and a C++ file, all of which manage together to make a DLL called pgfin.dll enabling PostgreSQL to value a vanilla option using the Black-Scholes formula found in QuantLib: ------------------------------------------------------ Makefile ------------------------------------------------------ INSTALL_DIR := $(shell pg_config --pkglibdir) EXTRA_LIBS = -lQuantLib EXTRA_LIB_PATHS = -L/cygdrive/D/Brian/QuantLib-0.3.6/ql/.libs #INSTALL_DIR = /usr/lib/postgresql/ CPP=g++ #Automatic DLL name cur-dir := $(shell pwd) TARGET_NAME = $(notdir $(cur-dir)) DLL_NAME=$(TARGET_NAME).dll DLL_DEBUG_NAME=$(TARGET_NAME)_d.dll DLL_DEBUG_PROF_NAME=$(TARGET_NAME)_pd.dll DEF_NAME=$(TARGET_NAME).def # Only needed for certain platforms # CFLAGS := $(CFLAGS) -fpic OTHER_HEADER_FLAGS = -I /usr/include/postgresql/server -I /cygdrive/D/Brian/QuantLib-0.3.6 -I. # Automatic inclusion of C and C++ files found CFILES := $(wildcard *.c) CPPFILES := $(wildcard *.cpp) OBJECT_DIR=obj DEBUG_OBJECT_DIR=obj_debug LIB_DIR=libs LIB_TARGET := $(DLL_NAME) DLIB_TARGET := $(DLL_DEBUG_NAME) PLIB_TARGET := $(DLL_DEBUG_PROF_NAME) DEF_PATH = $(LIB_DIR)/$(DEF_NAME) SRCFILES = $(CFILES) $(HFILES) $(CPPFILES) $(CPPFILES:.C=.h) OFILES = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) VPATH = $(OBJECT_DIR) ALL_CFLAGS = $(CFLAGS) $(OTHER_HEADER_FLAGS) -Wall default: library library: ${MAKE} "cur-dir=${cur-dir}" "CFLAGS=$(CFLAGS) -O" $(LIB_TARGET) debug: ${MAKE} "cur-dir=${cur-dir}" "CFLAGS=$(CFLAGS) -g" "OBJECT_DIR=$(DEBUG_OBJECT_DIR)" $(DLIB_TARGET) profile: ${MAKE} "cur-dir=${cur-dir}" "CFLAGS=$(CFLAGS) -pg" $(PLIB_TARGET) .c.o: $(CC) $(ALL_CFLAGS) -c $*.c -o $(OBJECT_DIR)/$*.o .cpp.o: $(CPP) $(ALL_CFLAGS) $(CPPFLAGS) -c $*.cpp -o $(OBJECT_DIR)/$*.o $(LIB_TARGET): $(LIB_DIR)/$(LIB_TARGET) $(DLIB_TARGET): $(LIB_DIR)/$(DLIB_TARGET) $(PLIB_TARGET): $(LIB_DIR)/$(PLIB_TARGET) $(LIB_DIR)/$(LIB_TARGET) $(LIB_DIR)/$(DLIB_TARGET) $(LIB_DIR)/$(PLIB_TARGET):$(LIB_DIR) $(OBJECT_DIR) $(OFILES) $(CFILES) $(SRCFILES) -/bin/rm $@ (cd $(OBJECT_DIR); dlltool --export-all --output-def ../$(DEF_PATH) $(OFILES); dllwrap --driver-name g++ -o ../$@ --dllname ../$@ --def ../$(DEF_PATH) $(OFILES) -L/lib $(EXTRA_LIB_PATHS) $(EXTRA_LIBS) -lpostgres ); chmod a+rx $@ $(OBJECT_DIR): mkdir $(OBJECT_DIR) $(LIB_DIR): mkdir $(LIB_DIR) objonly: $(OBJECT_DIR) $(OFILES) install: $(LIB_DIR)/$(LIB_TARGET) cp $(LIB_DIR)/$(LIB_TARGET) $(INSTALL_DIR); installdebug: $(LIB_DIR)/$(DLIB_TARGET) cp $(LIB_DIR)/$(DLIB_TARGET) $(INSTALL_DIR); # # Cleaning Rules # GARBAGE = *~ $(OBJECT_DIR)/*.o $(DEBUG_OBJECT_DIR)/*.o *.o *.def *.tab.h Makefile.dependencies Solaris_obj Solaris_debug_obj Solaris_profile_obj PRODUCTS = $(LIB_DIR)/$(LIB_TARGET) $(LIB_DIR)/$(DLIB_TARGET) $(LIB_DIR)/$(PLIB_TARGET) $(DEF_PATH) .PHONY: clean mostlyclean announce-clean clean: announce-clean $(RM) -rf $(GARBAGE) $(PRODUCTS) mostlyclean: announce-clean $(RM) -rf $(GARBAGE) announce-clean: $(SILENT) $(ECHO) == Cleaning $(TARGET_NAME) == ------------------------------------------------------ C File ------------------------------------------------------ #include "postgres.h" #include <string.h> #include "fmgr.h" #include "utils/geo_decls.h" extern double BS(int callput, double underlying, double strike, double volatility, double r, double q, double t); PG_FUNCTION_INFO_V1(BlackScholes); Datum BlackScholes(PG_FUNCTION_ARGS) { int32 callput = PG_GETARG_INT32(0); float8 spot = PG_GETARG_FLOAT8(1); float8 strike = PG_GETARG_FLOAT8(2); float8 vol = PG_GETARG_FLOAT8(3); float8 r = PG_GETARG_FLOAT8(4); float8 q = PG_GETARG_FLOAT8(5); float8 t = PG_GETARG_FLOAT8(6); float8 val=0.0; val = BS(callput,spot,strike, vol, r, q, t); PG_RETURN_FLOAT8(val); } ------------------------------------------------------ CPP File ------------------------------------------------------ // Basically stripped down from QuantLib example #include <ql/config.hpp> #include <ql/quantlib.hpp> using namespace QuantLib; extern "C" { double BS(int callput, double underlying, double strike, double volatility, double r, double q, double t) { Option::Type type(Option::Call); double value=-1; Spread dividendYield = q; Rate riskFreeRate = r; Date todaysDate(15, May, 1998); Date settlementDate(17, May, 1998); Date exerciseDate(17, May, 1999); DayCounter rateDayCounter = Actual365(); Time maturity = rateDayCounter.yearFraction(settlementDate, exerciseDate); Date midlifeDate(19, November, 1998); Handle<Exercise> exercise(new EuropeanExercise(exerciseDate)); RelinkableHandle<Quote> underlyingH( Handle<Quote>(new SimpleQuote(underlying))); RelinkableHandle<TermStructure> flatTermStructure( Handle<TermStructure>( new FlatForward(todaysDate, settlementDate, riskFreeRate, rateDayCounter))); RelinkableHandle<TermStructure> flatDividendTS( Handle<TermStructure>( new FlatForward(todaysDate, settlementDate, dividendYield, rateDayCounter))); RelinkableHandle<BlackVolTermStructure> flatVolTS( Handle<BlackVolTermStructure>( new BlackConstantVol(settlementDate, volatility))); Handle<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, strike)); Handle<BlackScholesStochasticProcess> stochasticProcess(new BlackScholesStochasticProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS)); VanillaOption option(stochasticProcess, payoff, exercise, Handle<PricingEngine>(new AnalyticEuropeanEngine())); option.setPricingEngine(Handle<PricingEngine>( new AnalyticEuropeanEngine())); value = option.NPV(); return value; } ------------------------------------------------------ SQL ------------------------------------------------------ CREATE FUNCTION BlackScholes(integer, double precision, double precision, double precision, double precision, double precision, double precision) RETURNS double precision AS 'pgfin', 'BlackScholes' LANGUAGE C STRICT; - Brian
В списке pgsql-cygwin по дате отправления:
Следующее
От: "Leeuw van der, Tim"Дата:
Сообщение: Re: Problems with installation of cygwin/postgreSQL