Отключение конструктора класса по умолчанию в модулях Rcpp - PullRequest
1 голос
/ 12 июня 2019

Я хотел бы отключить конструктор по умолчанию (нулевой аргумент) для класса C ++, доступного для R, с использованием RCPP_MODULE, чтобы при вызове new (класса) без каких-либо дополнительных аргументов в R выдавалась ошибка.Вот варианты, которые я пробовал:

1) Указание конструктора по умолчанию и выдача ошибки в теле функции: это делает то, что мне нужно, но означает указание конструктора по умолчанию, который устанавливает фиктивные значения для всехпеременные-члены const (что очень утомительно для моего реального случая использования)

2) Указание, что конструктор по умолчанию является закрытым (без определения): это означает, что код не будет компилироваться, если в .constructor () используется вмодуль Rcpp, но не действует, если .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)

Как я могу получить новый (mod $ Bar) с ошибкой, не прибегая к решению, используемому для Foo?


EDIT


Я обнаружил, что мой вопрос на самом деле является симптомомчего-то другого:

#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) вообще не вызывает никакого конструктора C ++ дляFoo, так что мой вопрос был несколько неосновательным ... извините.

Я думаю, что нет способа предотвратить это на уровне C ++, поэтому, возможно, имеет смысл использовать функцию-оболочку для вызована новый (Foo) на уровне R, или продолжайте указывать конструктор по умолчанию, который выдает ошибку.Оба эти решения будут работать нормально - мне просто было любопытно, почему мои ожидания в отношении отсутствующего конструктора по умолчанию были неправильными:)

В качестве дополнительного вопроса: кто-нибудь точно знает, что происходит в f0 <- новый (Foo) выше?Мое ограниченное понимание предполагает, что хотя f0 создается в R, связанный указатель приводит к чему-то, что не было (правильно / полностью) выделено в C ++? </p>

1 Ответ

1 голос
/ 16 июня 2019

После небольшого количества экспериментов я нашел простое решение моей проблемы, которое очевидно в ретроспективе ...!Все, что мне нужно было сделать, это использовать .factory для конструктора по умолчанию вместе с функцией, которая не принимает аргументов и просто выдает ошибку.На конструктор по умолчанию для класса никогда не ссылаются, поэтому его не нужно определять, но он получает желаемое поведение в 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, касающемсяк этому.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...