在Rcpp模块中禁用默认的类构造函数

时间:2019-06-12 12:35:35

标签: rcpp

我想使用RCPP_MODULE禁用暴露给R的C ++类的默认(零参数)构造函数,以便在R中没有任何其他参数的情况下调用new(class)会产生错误。这是我尝试过的选项:

1)指定一个默认构造函数并在函数体中引发错误:这是我需要做的,但是意味着指定一个默认构造函数,该默认构造函数为所有const成员变量设置伪值(这对于我的实际用例而言是乏味的)

2)指定默认构造函数是私有的(没有定义):这意味着如果在Rcpp模块中使用.constructor(),则代码将不会编译,但是如果未使用.constructor(),则该代码将无效。在Rcpp模块中

3)在默认构造函数上明确使用delete:需要C ++ 11,并且似乎具有与(2)相同的效果(缺少)

我确定我缺少明显的东西,但我一生无法解决它。有人有什么想法吗?

预先感谢

马特


最小示例代码(在R中运行):

inc <- '
using namespace Rcpp;

class Foo
{
public:
    Foo()
    {
        stop("Disallowed default constructor");
    }

    Foo(int arg)
    {
        Rprintf("Foo OK\\n");
    }
};

class Bar
{
private:
    Bar();
    // Also has no effect:
    // Bar() = delete;

public:
    Bar(int arg)
    {
        Rprintf("Bar OK\\n");
    }
};

RCPP_MODULE(mod) {

    class_<Foo>("Foo")
        .constructor("Disallowed default constructor")
        .constructor<int>("Intended 1-argument constructor")
    ;

    class_<Bar>("Bar")
        // Wont compile unless this line is commented out:
        // .constructor("Private default constructor")
        .constructor<int>("Intended 1-argument constructor")
    ;
} 
'

library('Rcpp')
library('inline')

fx <- cxxfunction(signature(), plugin="Rcpp", include=inc)
mod <- Module("mod", getDynLib(fx))

# OK as expected:
new(mod$Foo, 1)
# Fails as expected:
new(mod$Foo)

# OK as expected:
new(mod$Bar, 1)
# Unexpectedly succeeds:
new(mod$Bar)

如何在不求助于Foo的解决方案的情况下使new(mod $ Bar)失败?


编辑


我发现我的问题实际上是其他症状:

#include <Rcpp.h>

class Foo {
public:
    int m_Var;
    Foo() {Rcpp::stop("Disallowed default constructor"); m_Var=0;}
    Foo(int arg) {Rprintf("1-argument constructor\n"); m_Var=1;}
    int GetVar() {return m_Var;}
};

RCPP_MODULE(mod) {
    Rcpp::class_<Foo>("Foo")
        .constructor<int>("Intended 1-argument constructor")
        .property("m_Var", &Foo::GetVar, "Get value assigned to m_Var")
    ;
} 

/*** R
# OK as expected:
f1 <- new(Foo, 1)
# Value set in the 1-parameter constructor as expected:
f1$m_Var

# Unexpectedly succeeds without the error message:
tryCatch(f0 <- new(Foo), error = print)
# This is the type of error I was expecting to see:
tryCatch(f2 <- new(Foo, 1, 2), error = print)

# Note that f0 is not viable (and sometimes brings down my R session):
tryCatch(f0$m_Var, error = print)
*/

[向@RalfStubner致谢以提供改进的代码]

因此,实际上,似乎new(Foo)实际上根本没有为Foo调用任何C ++构造函数,所以我的问题有点离题了……抱歉。

我想没有办法防止在C ++级别上发生这种情况,因此也许最有意义的是在R级别上对new(Foo)的调用使用包装函数,或者继续指定一个默认构造函数,引发错误。这两个解决方案都可以正常工作-我只是好奇为什么我对缺少默认构造函数的期望是错误的:)

作为后续问题:是否有人确切知道上面的f0 <-new(Foo)中发生了什么?我有限的理解表明,尽管f0是在R中创建的,但关联的指针会导致某些东西(在C ++中尚未正确分配)?

1 个答案:

答案 0 :(得分:1)

经过一些试验,我发现了一个简单的解决方案,回想起来很明显……!我要做的就是将.factory用作默认构造函数,以及一个不带参数且只会引发错误的函数。默认情况下,Class的默认构造函数从未实际引用过,因此不需要定义,但它在R中获得了所需的行为(即,如果用户错误地调用了new而没有附加参数,则会产生错误)。

以下是显示解决方案的示例(Foo_A)和更清晰的问题说明(Foo_B):

#include <Rcpp.h>

class Foo {
private:
    Foo();   // Or for C++11:  Foo() = delete;
    const int m_Var;
public:
    Foo(int arg) : m_Var(arg) {Rcpp::Rcout << "Constructor with value " << m_Var << "\n";}
    void ptrAdd() const {Rcpp::Rcout << "Pointer: " << (void*) this << "\n";}
};

Foo* dummy_factory() {Rcpp::stop("Default constructor is disabled for this class"); return 0;}

RCPP_MODULE(mod) {
    Rcpp::class_<Foo>("Foo_A")
        .factory(dummy_factory)  // Disable the default constructor
        .constructor<int>("Intended 1-argument constructor")
        .method("ptrAdd", &Foo::ptrAdd, "Show the pointer address")
    ;

    Rcpp::class_<Foo>("Foo_B")
        .constructor<int>("Intended 1-argument constructor")
        .method("ptrAdd", &Foo::ptrAdd, "Show the pointer address")
    ;
} 

/*** R
# OK as expected:
fa1 <- new(Foo_A, 1)
# Error as expected:
tryCatch(fa0 <- new(Foo_A), error = print)

# OK as expected:
fb1 <- new(Foo_B, 1)
# No error:
tryCatch(fb0 <- new(Foo_B), error = print)
# But this terminates R with the following (quite helpful!) message:
# terminating with uncaught exception of type Rcpp::not_initialized: C++ object not initialized. (Missing default constructor?)
tryCatch(fb0$ptrAdd(), error = print)
*/

正如我在评论中建议的那样,我在https://github.com/RcppCore/Rcpp/issues/970上开始了与此相关的讨论。