Опасно ли подключать сигналы Qt5 к лямбде C ++ 14 с параметром `auto`? - PullRequest
0 голосов
/ 25 октября 2018

Мне поручено создать интерфейс для внешнего приложения, которое находится вне нашего контроля.Сейчас это приложение Qt5, и мы подключаемся к его сигналам, которые отправляют QExplicitlySharedDataPointer<T>.В следующие пару месяцев это внешнее приложение удалит свою зависимость от Qt, и они сказали, что, вероятно, переключатся на отправку std::shared_ptr<T> или чего-то подобного с помощью какого-либо другого метода события (у меня нет больше информации, чем длякак мы будем получать эти указатели в данный момент).

Моя цель - уменьшить количество изменений, которые нам нужно будет внести в нашу кодовую базу, когда они сделают это изменение.Они подтвердили, что не собираются менять интерфейс T, поэтому моей первой мыслью было: «Интересно, позволяет ли Qt5 использовать шаблоны со слотами».Ответ на это нет, но я не был обескуражен.Мне удалось найти обходной путь, но я не уверен, что это абсолютно безопасно.Я надеюсь, что кто-то здесь более знающий, чем я, сможет объяснить, что происходит в этом коде, и определить, безопасно это или нет.

Моя идея заключалась в том, чтобы использовать лямбда-выражения C ++ 14 с автоматическими параметрами для настройкиобработчик, который может принимать любой тип указателя.Эта функция может извлечь ссылку на объект (общий указатель все еще находится в области видимости, поэтому не нужно беспокоиться об удалении объекта и о том, что моя ссылка станет недействительной) и передать его функции, которая извлекает из него данные.Это должно отделить то, как мы получаем данные от того, как мы их извлекаем.

Вот класс, который я использовал, чтобы проверить это:

// testclass.h
class TestClass : public QObject
{
    // This is just a stand-in for the data type we'll be
    // receiving. The public interface for this class shouldn't
    // be changing.
    class TestData : public QSharedData
    {
        public:
        // The data in the class isn't important for now because
        // I'm assuming the interface to the pointed-to instance
        // will be the same
        TestData() : value(5) { }

        private:
        int value;
    };

    Q_OBJECT
    public:
        explicit TestClass(QObject *parent = nullptr);

    signals:
        void sendRawPointer(TestData* ptr);
        void sendStdShared(std::shared_ptr<TestData> ptr);
        void sendQtShared(QExplicitlySharedDataPointer<TestData> ptr);
};




// testclass.cpp
TestClass::TestClass(QObject *parent) : QObject(parent)
{
    // I know the pointed-to type will always be the same, so I 
    // can get a const& to it and use its interface without worrying
    // about the specific type of pointer being used
    auto extractData = [=](const TestData& data) {
        std::cout << "Received data: " << data.value << "\n";
    };

    // This is the key piece. It should allow me to receive any pointer
    // type that supports the dereference operator. That's all of the
    // pointer types I'm aware of, assuming they don't try to roll their
    // own specific for this class
    auto receivePointer = [=](auto ptr) {
        try {
            const auto& data = *ptr;
            extractData(data);
        } catch(...) {
            std::cerr << "Unable to receive data. The signal must use "
              << "a pointer type that supports operator*()\n";
        }
    };

    // EDIT: Forgot these lines originally.
    connect(this, &TestClass::sendRawPointer,
            receivePointer);
    connect(this, &TestClass::sendStdShared,
            receivePointer);
    connect(this, &TestClass::sendQtShared,
            receivePointer);

    auto rawPointer = new TestData();
    auto stdShared = std::shared_ptr<TestData>(new TestData());
    auto qtShared = QExplicitlySharedDataPointer<TestData>(new TestData());

    emit sendRawPointer(rawPointer);
    emit sendStdShared(stdShared);
    emit sendQtShared(qtShared);
};

Мои вопросы

  1. Это безопасно сделать?Я покидаю этот проект через несколько месяцев и удаление зависимости от Qt будет после того, как я уйду.Я пытаюсь оставить что-то на месте, чтобы оно не сломалось.Вероятно, потребуются изменения, потому что это не будут сигналы Qt, но было бы неплохо не полностью менять способ получения данных, вплоть до типа указателя.

  2. Еслиэто уместно, это правильный способ сделать это?Для меня это похоже на удивительное использование auto (хотя оно требует обработки исключений в случае получения типа, который не имеет operator*()).Есть ли лучший способ сделать это, на что я должен смотреть вместо этого?

...