Перегрузка -> оператор стрелки в экземпляре shared_ptr <interface>без чисто виртуального деструктора в интерфейсе - PullRequest
0 голосов
/ 14 февраля 2020

Я пытаюсь перегрузить оператор ->, чтобы в конечном итоге выполнить что-то вроде:

MyInterface *myInstance = (MyInterface *)(new A());
myInstance->Toggle(); //this works wonderfully

std::shared_ptr<Wrapper<MyInterface>> sharedPtrWrapper = std::make_shared<Wrapper<MyInterface>>(myInstance);

//the next line doesn't compile, I would like to achieve something like this, but even 
//sharedPtrWrapper.get()->Toggle(); 
//would be nice to achieve, is this possible? 
sharedPtrWrapper->Toggle(); 

//this works:
sharedPtrWrapper->operator->()->Toggle();

Примечание: я не могу контролировать MyInterface, не могу реализовать чистый виртуальный деструктор.

Вот что я попробовал (работает приведенный ниже код):

#import <memory>
#import <iostream>

struct MyInterface {
    virtual bool Toggle() = 0;
};

class A : public MyInterface {
public:
    bool Toggle() {
        stateEnabled = !stateEnabled;
        std::cout<<"current state " << stateEnabled << std::endl;
        return true;
    }
private:
    bool stateEnabled = false;
};

template <typename T>
class Wrapper {
private:
    T *unsafePointer = nullptr;
public:
    Wrapper<T>()
    { }

    T *operator->() const {
        return unsafePointer;
    }
    T *getInner() {
        return unsafePointer;
    }
    Wrapper<T>(T *stuff) {
        unsafePointer = stuff;
    }

    ~Wrapper<T>() {}
};

int main(int argc, const char * argv[]) {
    MyInterface *myInstance = (MyInterface *)(new A());
    myInstance->Toggle();


    Wrapper<MyInterface> wrapperS(myInstance);
    wrapperS->Toggle();


    std::shared_ptr<Wrapper<MyInterface>> sharedPtrWrapper = std::make_shared<Wrapper<MyInterface>>(myInstance);
    sharedPtrWrapper->operator->()->Toggle();
    sharedPtrWrapper.operator->()->operator->()->Toggle();

    sharedPtrWrapper.get()->operator->()->Toggle();

    (*sharedPtrWrapper).operator->()->Toggle();

    return 0;
}

Вывод:

current state 1
current state 0
current state 1
current state 0
current state 1
current state 0
Program ended with exit code: 0

Повторение: этот код не компилируется:

sharedPtrWrapper->Toggle(); 

Как заставить его скомпилироваться?

Редактировать : я использую обертку, потому что не могу контролировать MyInterface, я получаю ее из библиотеки, также shared_ptr<MyInterface> mySharedPointer = std::make_shared<MyInterface>(myInstance); не делает не компилируется из-за отсутствия чистого виртуального деструктора из вышеупомянутого интерфейса.

Edit2 : пример использования библиотеки в псевдокоде:

void firstcallbackFromLib(Framework *framework) {
    MyInterface *myInstance = framework->getInstance();

    {
        Wrapper<MyInterface> wrapperS(myInstance);
        std::shared_ptr<Wrapper<MyInterface>> sharedPtrWrapper = std::make_shared<Wrapper<MyInterface>>(wrapperS);
        //assign sharedPtrWrapper and framework to static instances
    }
}
void myFunction() {
    sharedPtrWrapper->Toggle(); //this doesn't work, this is what i'm trying to achieve
    sharedPtrWrapper->operator->()->Toggle(); //this ugly thing works

}
void lastUninitCallbackFromLibrary() {
    MyInterface *p = sharedPtrWrapper.get()->getInner();
    framework->releaseInterface(p);
    //etc
}

Ответы [ 5 ]

3 голосов
/ 14 февраля 2020

Проблема в том, что shared_ptr ведет себя как указатель, а Wrapper также. Таким образом, у вас есть код, который ведет себя как указатель на указатель. Короче говоря, вы могли бы назвать (*sharedPtrWrapper)->Toggle(); вместо мерзости sharedPtrWrapper->operator->()->Toggle();.

Осторожно, хотя: неясно, чего все это должно достигнуть, потому что пример кода является просто абстракцией вашего фактического кода. Поэтому, может быть, было бы более элегантно поместить метод пересылки Toggle() в класс Wrapper, но это невозможно определить с помощью приведенной здесь информации.

2 голосов
/ 14 февраля 2020

Тебе не нужна твоя обертка.

shared_ptr<MyInterface> mySharedPointer = std::make_shared<MyInterface>();

не будет работать, потому что MyInterface является абстрактным классом. Но, так же, как вы можете сделать

MyInterface *myInstance = new A();

Чтобы иметь MyInterface *, который указывает на конкретный производный объект, вы можете использовать

std::shared_ptr<MyInterface> sharedPtr = std::make_shared<A>();

Чтобы получить std::shared_ptr<MyInterface>, который указывает к конкретному производному объекту. Затем вы можете использовать sharedPtr для доступа к Toggle, как

sharedPtr->Toggle();

. Вы можете видеть, что работа в этом живом примере

2 голосов
/ 14 февраля 2020

Я запутался в вопросе. Почему класс-оболочка, который ничего не делает?

Если вы хотите поместить класс в общий указатель, все же сделать что-то необычное при уничтожении: например, вызвать функцию dll, которая выполняет уничтожение, выполнить некоторую предварительную обработку, выполнить закрытие файла вместо удаления или вообще ничего не делать, если вы этого хотите. Затем вы можете просто указать его при создании общего указателя: https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr - см. Вариант построения 5.

1 голос
/ 14 февраля 2020

Использование:

struct CleanupMyInterface {
  SomeTypeFromLib* somePointerFromLib = nullptr;
  void operator()( MyInterface* ptr ) const {
    if (somePointerFromLib && ptr)
      somePointerFromLib->releaseInterface(ptr);
  }
};
std::shared_ptr<MyInterface> sharedPtr( CreateAnInstanceOfAFromLibrary(), CleanupMyInterface{somePointerFromLib} );

shared_ptr имеет уничтожение по типу, виртуальный деструктор не нужен.

0 голосов
/ 14 февраля 2020

sharedPtrWrapper->Toggle(); не компилируется из-за operator-> правил цепочки, хорошо объясненных в этом ответе . В принципе: если ваш объект НЕ является указателем, operator-> вызывается рекурсивно, если это указатель, выполняется доступ к члену. Теперь std::shared_ptr перегружен operator-> для доступа к необработанному указателю Wrapper<MyInterface>*, который хранится внутри, и когда он применяется к нему, он пытается получить доступ к Toggle, которого не существует.

Для ясности обратите внимание, что этот код также не будет компилироваться:

Wrapper<MyInterface>* wrapper = new Wrapper<MyInterface>(myInstance);
wrapper->Toggle();

Однако вы можете сделать это:

(*sharedPtrWrapper)->Toggle();

...