jasonroelofs/rbgccxml

Problem wrapping a class with rbplusplus

Closed this issue · 7 comments

I am trying to wrap a class (goblinController) in the goblin library (http://goblin2.sourceforge.net/), and I get this error from rbgccxml:

ruby wrapper.rb --clean
(INFO) Parsing ["/Users/papipo/Projects/goblin/src/goblin_wrapper.h"]
(INFO) Beginning code generation
(INFO) Wrapping class goblin::goblinController
(WARNING) goblin::goblinController has multiple constructors. While the extension will probably compile, Rice only supports one constructor, please use #use_contructor to select which one to use.
(INFO) Wrapping class goblin::biGraph
(INFO) Code generation complete
(INFO) Writing code to files
/usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/builders/method_base.rb:165:in `fix_enumeration_value': undefined method `first' for #<RbGCCXML::EnumValue:0x000001045d5598> (NoMethodError)
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/builders/method_base.rb:121:in `block in wrap_normal_method'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/builders/method_base.rb:115:in `each'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/builders/method_base.rb:115:in `wrap_normal_method'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/builders/method_base.rb:38:in `write'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:45:in `process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:60:in `block in process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:59:in `each'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:59:in `process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:60:in `block in process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:59:in `each'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:59:in `process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:60:in `block in process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:59:in `each'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:59:in `process_code'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/writers/single_file_writer.rb:20:in `write'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/extension.rb:219:in `write'
    from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rbplusplus-1.0.1/lib/rbplusplus/extension.rb:84:in `initialize'
    from wrapper.rb:52:in `new'
    from wrapper.rb:52:in `<main>'

Very strange, I'll check out what's going on. Do you have the download link for goblin? I keep getting a Sourceforge error page when I try to download the library's source.

http://sourceforge.net/projects/goblin2/files/latest/download?source=files

Put "lib_src" and "include" dirs in a local "src" dir and use this as wrapper:

require 'rbplusplus'

SRC = File.expand_path(File.join(File.dirname(__FILE__), 'src'))

def constructor_for(klass, arguments = 0)
  klass.use_constructor(klass.constructors.find(arguments: Array.new(arguments.to_i)))
end

def constructors_for(klass, options)
  options.each_pair { |name,arguments| constructor_for(klass.classes(name), arguments) }
end

def structs(klass, options)
  options.each_pair { |name,arguments| constructor_for(klass.structs(name), arguments) }
end

File.open("#{SRC}/goblin_wrapper.h", "w") do |file|
  file.puts <<-SRC
  #ifndef GOBLIN_WRAPPER_H
  #define GOBLIN_WRAPPER_H

  #include <climits>
  #include <cfloat>
  #include <cstdlib>
  #include <cstdio>
  #include <cmath>
  #include <cstring>

  #include <iostream>
  #include <fstream>
  #include <iomanip>
  #include <exception>
  #include <new>
  #include <list>
  #include <vector>
  #include <limits.h>
  #include <stdlib.h>

  namespace goblin {

    #define _GOBLIN_MAJOR_VERSION_ 2
    #define _GOBLIN_MINOR_VERSION_ 8
    #define _GOBLIN_REVISION_      "b28"

    #include "goblin.h"
    #{Dir["#{SRC}/lib_src/*.cpp"].map { |cpp| "#include \"#{File.basename(cpp)}\""}.join("\n")}
  }

  #endif
  SRC
end

RbPlusPlus::Extension.new("goblin") do |e|
  e.writer_mode :single
  e.working_dir = 'ext/goblin'
  e.sources ["#{SRC}/goblin_wrapper.h"], include_paths: ["#{SRC}/include", "#{SRC}/lib_src"]

  e.module "Goblin" do |goblin|
    node = goblin.namespace "goblin"
    node.functions.ignore
    node.enumerations.ignore
    node.classes.ignore
    node.structs.ignore

    node.classes("biGraph").tap do |bigraph|
      bigraph.unignore
      bigraph.wrap_as("BiGraph")
      constructor_for(bigraph, 3)
    end

    node.classes("goblinController").tap do |goblin_controller|
      goblin_controller.unignore
      # goblin_controller.wrap_as("GoblinController")
      # constructor_for(controller, 1)
    end
  end
end


lines = File.readlines("ext/goblin/goblin.rb.cpp")
File.open("ext/goblin/goblin.rb.cpp", "w") do |file|
  lines.each do |line|
    next if line.match(/BackCast|back_cast|sparseBigraph\.h/)
    file.puts line
  end
end

Check out the last lines where I remove header files that rbplusplus adds before goblin_wrapper.h
Is there a way to avoid them so I don't need to remove them?

A couple of things I've noticed so far:

  • Your custom wrapper file should not be including .cpp files, and from what I see it should only need "goblin.h" to pull in all the appropriate header files in the library.
  • Only pulling in goblin.h does cause some problems, in that it has some operator new and operator deletes defined, which apparently cannot be put in a namespace. I was unable to get HEAP_MON undefined, so I commented them out.
  • That led me to biGraph, which I was unable to find anywhere in the code (but it is in doku/manual.tex?). Is this supposed to be abstractBiGraph?

GCC-XML was never meant to parse C++ object bodies, only the headers and definitions of objects, and as such rb++ can only work with C++ header information. Pulling in the cpp files may have caused some confusion that led to this odd EnumValue error (that I so far have been unable to reproduce).

For the HEAP_MON stuff you must tweak configuration.h. I think I generated that file by running ./configure.

BTW, I just removed the cpp includes here and I still get the EnumValue error :-/

Ok I found out what's going on here. Commit d6474de has the answer. Enumeration#children was changed to return a QueryResult, which has a side effect of returning the object if there's only one in the list. I should revert this change as you'll always want a raw array of EnumValues to work with (and is exactly what rb++ expects to happen). Looking through the goblin code I've found a few enums that have only one value in it, which is what would throw this error.

I'll get rbgccxml changed and a new gem pushed, should fix this issue.

Ok further diving into what's up here, I decided to update rb++ instead of changing rbgccxml back. The error was with arguments with default values that included a single-value enumeration. Grab the newest version of rb++ (1.0.3) and let me know if this works better for you.

It seems to fix this very issue. Now I've new stuff to handle, thanks :D