包装在 rice/ruby 中的纯虚拟 c++ class 在运行时引发 TypeError ("is not a class (Module)")
pure virtual c++ class wrapped in rice/ruby raises a TypeError ("is not a class (Module)") at runtime
我正在包装一个名为 essentia 的 C++ 库
作为使用 rice-ruby 包装器的 ruby 扩展。
在这个库中,有两个纯虚拟类调用
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>'
我不知道代码中有什么问题,我到处寻找答案都无济于事。
P.S。我的完整代码是 here
这更像是一个评论,但为了格式化,我把它作为一个答案。如果这是一个错误的假设,请告诉我,以便我可以将其删除。
我认为原因是 ruby 扩展希望名称空间大写,但事实并非如此。请尝试 alias namespaces:
namespace Essentia = essentia;
namespace Essentia::Streaming = essentia::streaming;
等这应该有所帮助。 注意 我尝试克隆存储库并自己完成,但是 ./wav
命令(如 README
中指定)失败。
已解决
正如我所想,这对我来说是一件非常愚蠢的事情。
我通过查看产生错误的 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
的模块,因此可能没有同名的 class。
我可以说我被许多不同的问题误导了,但我会避免这种情况:忘记我几天前编写了那个模块对我来说是非常愚蠢的 - 没有任何借口。吸取的教训是 ruby
不允许 module
和 class
同名,只会选择先到的人。这完全合乎逻辑,仅此而已。
我正在包装一个名为 essentia 的 C++ 库 作为使用 rice-ruby 包装器的 ruby 扩展。
在这个库中,有两个纯虚拟类调用
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>'
我不知道代码中有什么问题,我到处寻找答案都无济于事。
P.S。我的完整代码是 here
这更像是一个评论,但为了格式化,我把它作为一个答案。如果这是一个错误的假设,请告诉我,以便我可以将其删除。
我认为原因是 ruby 扩展希望名称空间大写,但事实并非如此。请尝试 alias namespaces:
namespace Essentia = essentia;
namespace Essentia::Streaming = essentia::streaming;
等这应该有所帮助。 注意 我尝试克隆存储库并自己完成,但是 ./wav
命令(如 README
中指定)失败。
已解决
正如我所想,这对我来说是一件非常愚蠢的事情。
我通过查看产生错误的 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
的模块,因此可能没有同名的 class。
我可以说我被许多不同的问题误导了,但我会避免这种情况:忘记我几天前编写了那个模块对我来说是非常愚蠢的 - 没有任何借口。吸取的教训是 ruby
不允许 module
和 class
同名,只会选择先到的人。这完全合乎逻辑,仅此而已。