我今天开始了一个小实验:我编写了一个C++类,该类依赖于其他一些库(ALGLIB,Eigen,内部工具),并且我想为该类创建Ruby包装器。我目前正在使用Rice来做到这一点。首先,我为我的类(class)写了一个非常简单的C++包装器:

// @file MLPWrapper.h
#pragma once

#include "mlp/MLP.h"
#include <ruby.h>

class MLPWrapper
{
  MLP mlp; // <- my C++ class
public:
  ...
  void fit()
  {
    ...
    mlp.fit(stop);
  }
};

库的cpp文件是这样的:
// @file cmlp.cpp
#include "rice/Data_Type.hpp"
#include "rice/Constructor.hpp"
#include "MLPWrapper.h"

using namespace Rice;

extern "C"
void Init_cmlp()
{
  Data_Type<MLPWrapper> rb_cMLPWrapper = define_class<MLPWrapper>("MLP")
      .define_constructor(Constructor<MLPWrapper>())
      ...
      .define_method("fit", &MLPWrapper::fit);
}

这是extconf.rb:
require "mkmf-rice"

$CFLAGS << "-O3"

HEADER_DIRS = [
  "..",
  "../../external/libraries/eigen-eigen-3.0.1",
  "../../external/libraries/alglib/cpp/src",
  "../../external/libraries/CMA-ESpp"
]
LIB_DIRS = [
  "../../build/external/libraries/alglib/cpp/src",
  "../../build/external/libraries/CMA-ESpp/cma-es",
  "../../build/src/tools"
]

dir_config("libs", HEADER_DIRS, LIB_DIRS)

have_library("libtools")
have_library("libalglib")

create_makefile("cmlp")

除链接外,其他一切正常。显然,库的头文件已包含在内,否则将无法编译。但是,当我在Ruby中运行一个小的测试程序(“require“cmlp”; mlp = MLP.new“)时,它找不到符号_ZN6LoggerC1ENS_6TargetESs,它是libtools(一个枚举)的一部分。这是我构建C++扩展并执行测试程序时发生的情况:
$ ruby extconf.rb
checking for main() in -lrice... yes
checking for main() in -llibtools... no
checking for main() in -llibalglib... no
checking for main() in -llibcmaes... no
creating Makefile
$ make
g++ -I. -I. -I/usr/lib/ruby/1.8/x86_64-linux -I. -I.. -I../../external/libraries/eigen-eigen-3.0.1 -I../../external/libraries/alglib/cpp/src -I../../external/libraries/CMA-ESpp     -I/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/include -fPIC -fno-strict-aliasing -g -g -O2  -fPIC -O3   -Wall -g -c cmlp.cpp
g++ -shared -o cmlp.so cmlp.o -L. -L/usr/lib -L../../build/external/libraries/alglib/cpp/src -L../../build/external/libraries/CMA-ESpp/cma-es -L../../build/src/tools -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic  -L/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/lib    -lrice -lruby1.8 -lpthread -lrt -ldl -lcrypt -lm   -lc
$ ruby test.rb
ruby: symbol lookup error: ./cmlp.so: undefined symbol: _ZN6LoggerC1ENS_6TargetESs

这些库都是使用CMake(add_library(...))编译的,位于
../../build/src/tools/libtools.so
../../build/external/libraries/alglib/cpp/src/libalglib.so
../../build/external/libraries/CMA-ESpp/cma-es/libcmaes.so

我不知道如何独自解决此问题,也找不到有关该问题的任何有用文档。如何修复extconf.rb?我感谢所有提示。

编辑:好的,我更改了extconf.rb:
require "rubygems"
require "mkmf-rice"

BASE_DIR = "/bla/"

$CFLAGS << " -O3"

dir_config("tools", [BASE_DIR + "src", BASE_DIR + "external/libraries/eigen-eigen-3.0.1"], BASE_DIR + "build/src/tools")
unless have_library("tools")
  abort "tools are missing. please compile tools"
end

dir_config("alglib", BASE_DIR + "external/libraries/alglib/cpp/src", BASE_DIR + "build/external/libraries/alglib/cpp/src")
unless have_library("alglib")
  abort "alglib is missing. please compile alglib"
end

dir_config("cmaes", BASE_DIR + "external/libraries/CMA-ESpp", BASE_DIR + "build/external/libraries/CMA-ESpp/cma-es")
unless have_library("cmaes")
  abort "cmaes is missing. please compile cmaes"
end

create_makefile("cmlp")

生成的Makefile是:
SHELL = /bin/sh

#### Start of system configuration section. ####

srcdir = .
topdir = /usr/lib/ruby/1.8/x86_64-linux
hdrdir = $(topdir)
VPATH = $(srcdir):$(topdir):$(hdrdir)
exec_prefix = $(prefix)
prefix = $(DESTDIR)/usr
sharedstatedir = $(prefix)/com
mandir = $(prefix)/share/man
psdir = $(docdir)
oldincludedir = $(DESTDIR)/usr/include
localedir = $(datarootdir)/locale
bindir = $(exec_prefix)/bin
libexecdir = $(prefix)/lib/ruby1.8
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
htmldir = $(docdir)
vendorarchdir = $(vendorlibdir)/$(sitearch)
includedir = $(prefix)/include
infodir = $(prefix)/share/info
vendorlibdir = $(vendordir)/$(ruby_version)
sysconfdir = $(DESTDIR)/etc
libdir = $(exec_prefix)/lib
sbindir = $(exec_prefix)/sbin
rubylibdir = $(libdir)/ruby/$(ruby_version)
docdir = $(datarootdir)/doc/$(PACKAGE)
dvidir = $(docdir)
vendordir = $(libdir)/ruby/vendor_ruby
datarootdir = $(prefix)/share
pdfdir = $(docdir)
archdir = $(rubylibdir)/$(arch)
sitearchdir = $(sitelibdir)/$(sitearch)
datadir = $(datarootdir)
localstatedir = $(DESTDIR)/var
sitelibdir = $(sitedir)/$(ruby_version)

CC = gcc
LIBRUBY = $(LIBRUBY_SO)
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
LIBRUBYARG_STATIC = -lruby1.8-static

RUBY_EXTCONF_H =
CFLAGS   =  -fPIC -fno-strict-aliasing -g -g -O2  -fPIC $(cflags) -O3
INCFLAGS = -I. -I. -I/usr/lib/ruby/1.8/x86_64-linux -I.
DEFS     =
CPPFLAGS =  -I/bla/external/libraries/CMA-ESpp -I/bla//external/libraries/alglib/cpp/src -I//bla/src -I/bla/external/libraries/eigen-eigen-3.0.1     -I/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/include
CXXFLAGS = $(CFLAGS)  -Wall -g
ldflags  = -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic  -L/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/lib
dldflags =
archflag =
DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
LDSHARED = g++ -shared
AR = ar
EXEEXT =

RUBY_INSTALL_NAME = ruby1.8
RUBY_SO_NAME = ruby1.8
arch = x86_64-linux
sitearch = x86_64-linux
ruby_version = 1.8
ruby = /usr/bin/ruby1.8
RUBY = $(ruby)
RM = rm -f
MAKEDIRS = mkdir -p
INSTALL = /usr/bin/install -c
INSTALL_PROG = $(INSTALL) -m 0755
INSTALL_DATA = $(INSTALL) -m 644
COPY = cp

#### End of system configuration section. ####

preload =

CXX = g++
libpath = . $(libdir) /bla/external/libraries/CMA-ESpp/cma-es /bla/build/external/libraries/alglib/cpp/src /bla/build/src/tools
LIBPATH =  -L. -L$(libdir) -L/bla/build/external/libraries/CMA-ESpp/cma-es -L/bla/build/external/libraries/alglib/cpp/src -L/bla/build/src/tools
DEFFILE =

CLEANFILES = mkmf.log
DISTCLEANFILES =

extout =
extout_prefix =
target_prefix =
LOCAL_LIBS =
LIBS = -lcmaes -lalglib -ltools -lrice -lruby1.8 -lpthread -lrt -ldl -lcrypt -lm   -lc
SRCS = cmlp.cpp
OBJS = cmlp.o
TARGET = cmlp
DLLIB = $(TARGET).so
EXTSTATIC =
STATIC_LIB =

BINDIR        = $(bindir)
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
RUBYLIBDIR    = $(sitelibdir)$(target_prefix)
RUBYARCHDIR   = $(sitearchdir)$(target_prefix)

TARGET_SO     = $(DLLIB)
CLEANLIBS     = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map
CLEANOBJS     = *.o *.a *.s[ol] *.pdb *.exp *.bak

all:        $(DLLIB)
static:     $(STATIC_LIB)

clean:
        @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)

distclean:  clean
        @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
        @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)

realclean:  distclean
install: install-so install-rb

install-so: $(RUBYARCHDIR)
install-so: $(RUBYARCHDIR)/$(DLLIB)
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
    $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
install-rb: pre-install-rb install-rb-default
install-rb-default: pre-install-rb-default
pre-install-rb: Makefile
pre-install-rb-default: Makefile
$(RUBYARCHDIR):
    $(MAKEDIRS) $@

site-install: site-install-so site-install-rb
site-install-so: install-so
site-install-rb: install-rb

.SUFFIXES: .c .m .cc .cxx .cpp .C .o

.cc.o:
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<

.cxx.o:
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<

.cpp.o:
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<

.C.o:
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<

.c.o:
    $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<

$(DLLIB): $(OBJS) Makefile
    @-$(RM) $@
    $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)



$(OBJS): ruby.h defines.h

最佳答案

../../build/src/tools/libtools.so
../../build/external/libraries/alglib/cpp/src/libalglib.so
../../build/external/libraries/CMA-ESpp/cma-es/libcmaes.so

可能是问题所在。我会在这里尝试绝对路径,我可以想象pwd与运行ruby extconf.rb时所期望的不同。
另外,假设您要链接的每个库都需要一个dir_config条目。因此
dir_config('libs', HEADER_DIRS, LIB_DIRS)

应该由
dir_config('tools', '<Path to include dir>', '<Path to lib dir>')
dir_config('alglib', '<Path to include dir>', '<Path to lib dir>')

请注意,就像在-l链接器选项中一样,应省略开头的“lib”。
接下来,如果您要绝对确定找到了一个库,请替换
have_library('libtools')

通过
have_library('tools') or raise

同样,省略了“lib”。

如果仍然不能解决问题,请发布`ruby extconf.rb'生成的Makefile。

07-24 14:24