Почему g cc не девиртуализируется через пару связанных CRTP-иерархий? - PullRequest
0 голосов
/ 01 апреля 2020

У меня есть пара типов, разделяющих свободные отношения клиент / сервер. Типы нетривиальны, поэтому, чтобы сделать их тестируемыми на модуле, я разделил их по аспектам с помощью декоратора. Каждый декоратор добавляет один аспект функциональности, такой как блокировка API с мьютексом, автоматическая регистрация c, повторение операций при сбое и тому подобное. Цепочка декоратора не изменяется во время выполнения, поэтому я пытался express использовать ее с помощью crtp и девиртуализированной поли-среды выполнения.

Я нашел случай, когда я думаю, что g cc должен быть в состоянии девиртуализировать, но это только частично. Если я помогу с обходным решением, он удастся девиртуализировать, определив методы пересылки непосредственно в типах листьев. Clang девиртуализируется без обходного пути.

Я что-то не так делаю, возможно, вызываю UB, или это g cc?

В приведенном ниже примере типы root не украшены, украшенные типы украшают корни, а типы листьев являются окончательными. Корни параметризуются на листьях для crtp, декораторы дополнительно параметризуют то, что они украшают, и листья непараметризованы, предназначенные для производства. Все API-интерфейсы здесь представлены в терминах типов листьев.

g cc девиртуализируется только тогда, когда определено ENABLE_WORKAROUND.

ссылка Godbolt: https://godbolt.org/z/V85CoZ

#include <cstdint>

// #define ENABLE_WORKAROUND

template <typename leaf_client>
class root_server
{
public:
    virtual auto associate(leaf_client& client) noexcept -> void { client_ = &client; }
    virtual auto on_operation(leaf_client& client) noexcept -> std::intptr_t
    {
        return reinterpret_cast<std::intptr_t>(&client);
    }

private:
    leaf_client* client_{};
};

template <typename leaf_client, typename leaf_server, typename undecorated_server>
class decorated_server : public undecorated_server
{
public:
    auto associate(leaf_client& client) noexcept -> void override
    {
        undecorated_server::associate(client);
        client.on_associate(static_cast<leaf_server&>(*this));
    }

    auto on_operation(leaf_client& client) noexcept -> std::intptr_t override
    {
        return undecorated_server::on_operation(client);
    }
};

template <typename leaf_client, typename leaf_server>
class root_client
{
public:
    virtual auto on_associate(leaf_server& server) noexcept -> void { server_ = &server; }
    virtual auto operation() -> std::intptr_t { return server_->on_operation(static_cast<leaf_client&>(*this)); }

private:
    leaf_server* server_{};
};

template <typename leaf_client, typename leaf_server, typename undecorated_client>
class decorated_client : public undecorated_client
{
public:
    auto on_associate(leaf_server& server) noexcept -> void override { undecorated_client::on_associate(server); }
    auto operation() -> std::intptr_t override { return undecorated_client::operation(); }
};

class leaf_client;
class leaf_server;

using base_server = decorated_server<leaf_client, leaf_server, root_server<leaf_client>>;
class leaf_server final : public base_server
{
public:
#if defined ENABLE_WORKAROUND
    auto on_operation(leaf_client& client) noexcept -> std::intptr_t override
    {
        return base_server::on_operation(client);
    }
#endif
};

using base_client = decorated_client<leaf_client, leaf_server, root_client<leaf_client, leaf_server>>;
class leaf_client final : public base_client
{
public:
#if defined ENABLE_WORKAROUND
    auto on_associate(leaf_server& server) noexcept -> void override { return base_client::on_associate(server); }
#endif
};

auto main() -> int
{
    auto server = leaf_server{};
    auto client = leaf_client{};
    server.associate(client);
    return client.operation();
}
...