Сохранение типа и дальнейшее приведение к нему (?)

Было необходимо реализовать класс-обертку для указателя на функцию (с любым числом любых параметров) для ее дальнейшего вызова. Так же было необходимо, чтобы объекты этого класса можно было помещать в контейнеры, поэтому пришлось избегать использования шаблонного класса, что очень упростило бы все (да и так уже есть std::function).

Вышло как-то так:

class COperation
{
public:
    template<typename ReturnType, typename ...Operands>
    COperation(ReturnType (*function)(Operands...)) {
        m_functionInterface = new Concreate<ReturnType, Operands...>(function);
    }
    ~COperation() {
        //delete m_functionInterface; // Почему-то падает
    }

public:
    template<typename ...Operands>
    void execute(Operands... args)
    {
        std::vector<std::any> argList;
        (argList.push_back(args), ...);
        m_functionInterface->execute(argList);
    }
    size_t operandCount() const
    {
        return  m_functionInterface->operandCount();
    }

private:
    struct Interface
    {
        virtual ~Interface() = default;

        virtual size_t operandCount() const = 0;
        virtual void execute(std::vector<std::any>) = 0;
    };

    template<typename R, typename ...Args>
    struct Concreate : Interface
    {
        explicit Concreate(R (*function)(Args...))
        {
            m_operandCount = sizeof...(Args);
            m_function = function;
        }

        size_t operandCount() const override
        {
            return m_operandCount;
        }

        void execute(std::vector<std::any> argList) override
        {
            if (argList.size() != sizeof...(Args)) {
                throw std::runtime_error("Incorrect number of arguments.");
            }

            auto applyArgums = [argList](auto&&... args)
            {
                qsizetype n = 0;
                ((args = std::any_cast<typeof(args)>(argList.at(n++))), ...);
            };
            std::apply(applyArgums, m_operands);
            result = std::apply(m_function, m_operands);
            qDebug() << result;
        }

    private:
        R (*m_function)(Args...);
        std::tuple<Args...> m_operands;
        qsizetype m_operandCount;
        R result;
    };

private:
    Interface* m_functionInterface;
};

Функции для примера:

inline constexpr qreal addition(qreal left, qreal rigth)
{
    return left + rigth;
}

inline constexpr qreal unaryMinus(qreal operand)
{
    return -operand;
}

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

COperation a(addition);
COperation um(unaryMinus);

a.execute(10., 20.); // 30
um.execute(10.);     // -10

Понятно, что много всего еще можно сделать, но я уже наткнулся на проблему. Из примера видно, что приходится явно передавать параметры нужного типа, так как в противном случае ловим bad any_cast тут.

((args = std::any_cast<typeof(args)>(argList.at(n++))), ...);

Собственно, в этом и есть главная проблема, которую не удается мне решить. В "std::tuple<Args...> m_operands" хранятся аргументы функции "R(*m_function)(Args...);". Нужен механизм, позволяющий преобразовать входные в "execute" параметры к типам из "std::tuple<Args...> m_operands", если это возможно. То есть, например, int мы должны преобразовывать в double (и наоборот), так как это возможно, а не ловить ошибку, как сейчас. Можно ли это как-то реализовать в рамках std::auto? Либо, возможно, как-то переделать функцию "execute"? Из рабочих вариантов было использование std::variant, но мне бы не хотелось ограничиваться типами, которые мы сами указываем. Возможно, у вас будут идеи?)


Ответы (0 шт):