0

I am trying to create a Python wrapper for a C++ library fbow using Boost.Python. My code is inspired from another wrapper pyDBoW3 to a very similar library DBoW3. Here is what I am able to do so far: The pyBoW3 wrapper works on my system - I can import it into Python and have it do its thing. The pyfbow wrapper that I am trying to create works in the barebones, however as soon as I try to link a function that uses something from the underlying fbow library it throws:

$ python -c "import pyfbow.so"
ImportError: dlopen(/Users/vik748/pyfbow/build/pyfbow.so, 2): Symbol not found: __ZN4fbow10Vocabulary10saveToFileERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
  Referenced from: /Users/vik748/pyfbow/build/pyfbow.so
  Expected in: flat namespace
 in /Users/vik748/pyfbow/build/pyfbow.so

$ nm pyfbow.dylib | grep __ZN4fbow10Vocabulary10saveToFileERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
                 U __ZN4fbow10Vocabulary10saveToFileERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE


$ c++filt -n __ZN4fbow10Vocabulary10saveToFileERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE
fbow::Vocabulary::saveToFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)

The main snippet of the wrapper code is:

class Vocabulary
{
public:
    Vocabulary(int k = 10, int L = 6, int nthreads = 1, int maxIters = 0, bool verbose = true){
        voc_creator_params.k = k;
        voc_creator_params.L = L;
        voc_creator_params.nthreads = nthreads;
        voc_creator_params.maxIters = maxIters;
        voc_creator_params.verbose = verbose;
    }
    ~Vocabulary() {
        //delete voc;
        //delete voc_creator;
    }
    void create(const  std::vector<cv::Mat>   &training_features ) {
        std::cout << "Creating a " << voc_creator_params.k << "^" << voc_creator_params.L << " vocabulary..." << std::endl;
       voc_creator->create(*voc, training_features, std::string("NA"), voc_creator_params);
    }

    fbow::Vocabulary * voc;
    fbow::VocabularyCreator * voc_creator;
    fbow::VocabularyCreator::Params voc_creator_params;
};

namespace fs {
    namespace python {
        BOOST_PYTHON_MODULE(pyfbow)
        {
            // Main types export
            fs::python::init_and_export_converters();
            py::scope scope = py::scope();

            // Class
            py::class_<Vocabulary>("Vocabulary")
                .def(py::init< py::optional<int, int, int, int, bool> >(
                    (py::arg("k") = 10, py::arg("L") = 5, py::arg("nthreads") = 1,
                     py::arg("maxIters") = 0, py::arg("verbose") = true )))
                .def("create", &Vocabulary::create);
        }
    } // namespace fs
} // namespace python

If you'd like to test on your system the only dependencies are boost-python and opencv. There is a build script on the repo at https://github.com/vik748/pyfbow that can get it going very quickly.

Now if I comment out voc_creator->create(*voc, training_features, std::string("NA"), voc_creator_params); everything works, code comiles, links and I am able to import the package into Python. However if the line is left uncommented, the code compiles and links but importing into python throws the error above.

Here are all the things I have tried:

  1. As described here, I have tried adding boost::noncopyable
  2. As described here, I have added code to a couple empty virtual functions that the underlying fbow library was using.
  3. I considered this suggesting that different compilers might be causing issues, but since the code compiles with the line commented, I don't think this is the issue.
  4. Going by the suggestion from here, I also tried combining both pyfbow and fbow into the same package and CMakeLists.txt but that didn't help either.
  5. Additionally, as per a couple different other SO posts, I have tried a bunch of linker flags -Wl -export_dynamic -flat_namespace -export-dynamic etc.

I am at my wits end trying to figure this out, I welcome any debugging ideas. Cheers!

As per @ead 's suggestion, I am adding output from ld --trace-symbol

ld pyfbow.so --trace-symbol=_ZN4fbow17VocabularyCreator6createERNS_10VocabularyERKSt6vectorIN2cv3MatESaIS5_EERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS0_6ParamsE
pyfbow.so: reference to _ZN4fbow17VocabularyCreator6createERNS_10VocabularyERKSt6vectorIN2cv3MatESaIS5_EERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS0_6ParamsE
ld: warning: cannot find entry symbol _start; not setting start address
pyfbow.so: undefined reference to `fbow::VocabularyCreator::create(fbow::Vocabulary&, std::vector<cv::Mat, std::allocator<cv::Mat> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, fbow::VocabularyCreator::Params)'
Vik
  • 690
  • 7
  • 22
  • 1
    Rather than trying out different suggestions without understanding the problem, you have to look at your linker-command (and maybe put it here) and analyze it, there is a collection of possible reasons: https://stackoverflow.com/q/12573816/5769463 Use `--trace-symbol` (--trace-symbol=symbol ) to trace in which order linker sees the symbol. – ead May 11 '20 at 07:10
  • @ead I have added the output from --trace-symbol, any other ideas to get to the root cause? – Vik May 11 '20 at 14:54
  • my suggestion was to add to your linker flags, so you understand what happens while the extension is linked, not after it has been created. Do you know how your linker-command looks like? Did you go through possible causes in the question I have linked and compared it to you link-command? – ead May 11 '20 at 14:59
  • so when I add it to the CMakeLists.txt as `SET_TARGET_PROPERTIES(pyfbow PROPERTIES PREFIX "" LINK_FLAGS "--trace-symbol=_ZN4fbow17VocabularyCreator6createERNS_10VocabularyERKSt6vectorIN2cv3MatESaIS5_EERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS0_6ParamsE")` It throws a `c++: error: unrecognized command line option ‘--trace-symbol=`, So what is the correct way to add this to CMakeLists.txt? I am also working through the suggestions in the link you provided. – Vik May 11 '20 at 15:33
  • If you use g++ Fronten to link, you can read the docs how to pass options to the linker, probably it is prefix „-Wl,“ or similar – ead May 11 '20 at 15:44

1 Answers1

0

TLDR: The fbow library findcmake was setting the fbar_LIBS variable while my wrapper was reading the fbar_LIBRARIES variable.

How to debug: As per suggestions from @ead and the link provided, I added set( CMAKE_VERBOSE_MAKEFILE on) parameter in the CMakeLists.txt and on running make I was able to look at the cmake_link_script. I compared this to the one from the pyDBoW3 package and saw that there were fewer -L parameters. On investigating further I found that fbow_LIBRARIES was empty. On checking the package's fbowConfig.cmake, it was setting the fbow_LIBS variable instead. Switching the names fixed all the issues.

Thanks @ead for your help.

Vik
  • 690
  • 7
  • 22