Чистый виртуальный класс C ++, обернутый в rice / ruby, вызывает ошибку TypeError («не является классом (модулем)») во время выполнения

Я нахожусь в процессе упаковки библиотеки C ++ под названием эссенция
как рубиновое расширение, используя рисово-рубиновую обертку.

В этой библиотеке есть два чистых виртуальных класса
essentia::standard::Algorithm а также essentia::streaming::Algorithm, Я следовал инструкциям и создал следующий код:

algorithm.hpp

#if !defined(_RICE_ESSENTIA_ALGORITHM_HPP_)
# define _RICE_ESSENTIA_ALGORITHM_HPP_

#include "essentia/algorithm.h"#include "essentia/streaming/streamingalgorithm.h"
#include "rice/Director.hpp"
namespace Rice
{
namespace Essentia
{
namespace Standard
{
class AlgorithmProxy : public essentia::standard::Algorithm, public Rice::Director
{

public:
AlgorithmProxy(Rice::Object self) : Rice::Director(self) {}

virtual void compute()
{
getSelf().call("compute");
}

void default_compute()
{
raisePureVirtual();
}

virtual void reset()
{
getSelf().call("reset");
}

void default_reset()
{
essentia::standard::Algorithm::reset();
}

virtual void declareParameters()
{
getSelf().call("declare_parameters");
}

void default_declareParameters()
{
raisePureVirtual();
}
};

void install_algorithm();
}

namespace Streaming
{
class AlgorithmProxy : public essentia::streaming::Algorithm, public Rice::Director
{

public:
AlgorithmProxy(Rice::Object self) : Rice::Director(self) {}

virtual essentia::streaming::AlgorithmStatus process()
{
return from_ruby<essentia::streaming::AlgorithmStatus>(getSelf().call("process"));
}

essentia::streaming::AlgorithmStatus default_process()
{
raisePureVirtual();
return essentia::streaming::AlgorithmStatus::FINISHED;
}

virtual void reset()
{
getSelf().call("reset");
}

void default_reset()
{
essentia::streaming::Algorithm::reset();
}

virtual void shouldStop(bool stop)
{
getSelf().call("should_stop");
}

void default_shouldStop(bool stop)
{
essentia::streaming::Algorithm::shouldStop(stop);
}

virtual bool shouldStop() const
{
return from_ruby<bool>(getSelf().call("should_stop?"));
}

bool default_shouldStop() const
{
return essentia::streaming::Algorithm::shouldStop();
}

virtual void declareParameters()
{
getSelf().call("declare_parameters");
}

void default_declareParameters()
{
raisePureVirtual();
}
};

void install_algorithm();
}
}
}

#endif /* !defined(_RICE_ESSENTIA_ALGORITHM_HPP_) */

а также

algorithm.cpp

#include "rice/Data_Type.hpp"#include "rice/Enum.hpp"#include "rice/Constructor.hpp"
#include "exception.hpp"#include "modules.hpp"#include "algorithm.hpp"
namespace Rice
{
namespace Essentia
{
namespace Standard
{

static Rice::Data_Type<essentia::standard::Algorithm> standard_algorithm_type;

void
install_algorithm()
{
RUBY_TRY
{
standard_algorithm_type =
define_class_under<essentia::standard::Algorithm>(essentia_standard_module(), "Algorithm")
.define_director<AlgorithmProxy>()
.define_constructor(Rice::Constructor<AlgorithmProxy, Rice::Object>())
.add_handler<essentia::EssentiaException>(handle_essentia_exception)
.define_method("reset", &AlgorithmProxy::default_reset)
.define_method("compute", &AlgorithmProxy::default_compute)
.define_method("input_names", &AlgorithmProxy::inputNames)
.define_method("output_names", &AlgorithmProxy::outputNames)
.define_method("input_types", &AlgorithmProxy::inputTypes)
.define_method("output_types", &AlgorithmProxy::outputTypes)
.define_method("declare_parameters", &AlgorithmProxy::default_declareParameters)
;
}
RUBY_CATCH
}

}

namespace Streaming
{

static Rice::Enum<essentia::streaming::AlgorithmStatus> algorithm_status_type;

void
install_algorithm_status()
{
algorithm_status_type =
define_enum<essentia::streaming::AlgorithmStatus>("AlgorithmStatus", essentia_streaming_module())
.define_value("OK", essentia::streaming::AlgorithmStatus::OK)
.define_value("CONTINUE", essentia::streaming::AlgorithmStatus::CONTINUE)
.define_value("PASS", essentia::streaming::AlgorithmStatus::PASS)
.define_value("FINISHED", essentia::streaming::AlgorithmStatus::FINISHED)
.define_value("NO_INPUT", essentia::streaming::AlgorithmStatus::NO_INPUT)
.define_value("NO_OUTPUT", essentia::streaming::AlgorithmStatus::NO_OUTPUT)
;
}

static Rice::Data_Type<essentia::streaming::Algorithm> streaming_algorithm_type;
typedef void (AlgorithmProxy::*set_should_stop)(bool);
typedef bool (AlgorithmProxy::*get_should_stop)(void) const;

void
install_algorithm()
{
RUBY_TRY
{
streaming_algorithm_type =
define_class_under<essentia::streaming::Algorithm>(essentia_streaming_module(), "Algorithm")
.define_director<AlgorithmProxy>()
.define_constructor(Rice::Constructor<AlgorithmProxy, Rice::Object>())
.add_handler<essentia::EssentiaException>(handle_essentia_exception)
.define_method("reset", &AlgorithmProxy::default_reset)
.define_method("input_names", &AlgorithmProxy::inputNames)
.define_method("output_names", &AlgorithmProxy::outputNames)
.define_method("should_stop", set_should_stop(&AlgorithmProxy::default_shouldStop))
.define_method("should_stop?", get_should_stop(&AlgorithmProxy::default_shouldStop))
.define_method("disconnect_all", &AlgorithmProxy::disconnectAll)
.define_method("process", &AlgorithmProxy::default_process)
.define_method("declare_parameters", &AlgorithmProxy::default_declareParameters)
;
install_algorithm_status();
}
RUBY_CATCH
}

}
}
}

пока мой Init_ код выглядит следующим образом:

init.cpp

#include "modules.hpp"#include "setup.hpp"#include "types.hpp"#include "exception.hpp"#include "algorithm.hpp"#include "io.hpp"
extern "C" {

void Init_essentia_ruby_wrap()
{
Rice::Essentia::create_essentia_modules();
Rice::Essentia::install_essentia_types();
Rice::Essentia::setup_essentia();
Rice::Essentia::Standard::install_io();
Rice::Essentia::Standard::install_algorithm();
Rice::Essentia::Streaming::install_algorithm();
}

}

Все компилируется нормально (используя clang++ -std=c++1y) но потом, когда я пытаюсь запустить полученный код, я получаю:

eeepc-1215B:.../essentia-ruby$ ruby -I./lib/essentia -e "require 'essentia_ruby_wrap'"/home/nicb/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': Essentia::Streaming::Algorithm is not a class (Module) (TypeError)
from /home/nicb/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from -e:1:in `<main>'

Я не могу понять, что не так в коде, и я везде искал ответ безрезультатно.

Постскриптум мой полный код Вот

1

Решение

Это было бы больше комментарием, но я поместил это как ответ ради форматирования. Пожалуйста, дайте мне знать, если это неверное предположение, чтобы я мог удалить его.

Я полагаю, что причина в том, что расширение ruby ​​ожидает, что пространства имен будут прописными, а они — нет. Пожалуйста, попробуйте пространства имен псевдонимов:

namespace Essentia = essentia;
namespace Essentia::Streaming = essentia::streaming;

и т.д. Это должно помочь. NB Я пытался клонировать репо и сделать это сам, но ./wav команды (как указано в README) не удалось.

0

Другие решения

Как я и думал, это было действительно глупо (для меня).

Я наткнулся на решение, глядя на ruby код, выдавший ошибку:

class.c (строки 641-660)

642 VALUE
643 rb_define_class(const char *name, VALUE super)
644 {
645     VALUE klass;
646     ID id;
647
648     id = rb_intern(name);
649     if (rb_const_defined(rb_cObject, id)) {
650     klass = rb_const_get(rb_cObject, id);
651     if (!RB_TYPE_P(klass, T_CLASS)) {
652         rb_raise(rb_eTypeError, "%s is not a class (%"PRIsVALUE")",
653              name, rb_obj_class(klass));
654     }
655     if (rb_class_real(RCLASS_SUPER(klass)) != super) {
656         rb_raise(rb_eTypeError, "superclass mismatch for class %s", name);
657     }
658     return klass;
659     }

и в документации, написанной непосредственно выше, говорится:

throws a TypeError if the constant name is already taken but
the constant is not a Class.

и мне пришло в голову, что, пытаясь подражать с ruby соглашения дерево каталогов essentia (таким образом собирая модуль для каждого каталога) уже создал модуль под названием Essentia::Streaming::Algorithm и, следовательно, не могло быть никакого класса с таким же именем.

Я мог бы сказать, что меня сбило с толку множество разных проблем, которые возникли, но я буду избегать этого: с моей стороны было просто глупо забывать, что я кодировал этот модуль несколько дней назад — никаких оправданий. Урок заключается в том, что ruby не позволит module и class с таким же именем, и просто подберут, кто бы ни пришел первым. Это совершенно логично и все.

0

По вопросам рекламы [email protected]