Замена (или повторная реализация?) std::function для некоторой статистики и тестирования

#c #visual-c #std #c 17 #dynamic-import

#c #visual-c #std #c 17 #динамический импорт

Вопрос:

У меня есть особый случай, когда я хотел бы измерить статистику использования моего std ::function. Обычно такие вещи, как затраченное время, количество вызовов, какая функция вызывается часто, а также иногда для тестирования, например, случайного выбрасывания nullptr, чтобы проверить, как мой код справляется с этим.

Сначала я думал о наследовании от std ::func, а затем переопределить функцию, которая извлекает указатель — или что-то подобное — но после прочтения нескольких тем и руководств я пришел к выводу, что это очень нецелесообразно.

Моя вторая мысль — просто создать класс-оболочку. Рефакторинг всего моего кода для использования этой оболочки вместо std::function не составит труда, так что все в порядке. Все, чего я хочу избежать, это использовать ненужное и неудобное использование, например, любую дополнительную функцию. например.:

 std::function<...> dummyvar (...);

// Later I can call this dummy var as simply as dummyvar(...)
// I don't want to add extra complexity, like this:

CustomWrapper<...> customdummy (...);
customdummy.getVal()(...);

// Best would be if I could actually "simply" replace all std::function to the Custom Wrapper.
  

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


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

Ответ №1:

Вы могли бы сделать что-то вроде:

 template <typename Sig>
class CustomWrapper
{
private:
    std::function<Sig> f;
    mutable std::size_t nbCall = 0;
    // ...
public:
    CustomWrapper() = default;

    CustomWrapper(const CustomWrapperamp;) = default;
    CustomWrapper(CustomWrapperamp;amp;) = default;
    CustomWrapperamp; operator=(const CustomWrapperamp;) = default;
    CustomWrapperamp; operator=(CustomWrapperamp;amp;) = default;

    template <typename T,
              std::enable_if_t<!std::is_same<CustomWrapper, std::decay_t<T>>::value
                  amp;amp; std::is_constructible<std::function<Sig>, Tamp;amp;>::value, bool> = false>
    CustomWrapper(Tamp;amp; arg) : f(std::forward<T>(arg)){}

    template <typename ... Ts,
              std::enable_if_t<(sizeof...(Ts) >= 2)
                  amp;amp; std::is_constructible<std::function<Sig>, Tsamp;amp;...>::value, bool> = false>
    CustomWrapper(Tsamp;amp; ... args) : f(std::forward<Ts>(args)...){}

    template <typename ... Ts>
    auto operator ()(Tsamp;amp;... args) const -> decltype(f(std::forward<Ts>(args)...))
    {
          nbCall; // Statistics you want
        // ...
        return f(std::forward<Ts>(args)...);
    }

    std::size_t getNbCall() const { return nbCall; }
    // ...
};
  

ДЕМОНСТРАЦИЯ

Комментарии:

1. Я довольно долго пытался скомпилировать ваш исходный код, когда понял, что он, похоже, работает только на clang. У GCC и VC есть проблемы с параметром __stdcall в определении функции. Знаете ли вы какое-либо решение проблемы?

2. @Unc3nZureD: я исправляю некоторые опечатки и предоставляю демонстрационную ссылку. Так что работайте над gcc / clang.

3. Используете ли вы явно __stdcall ?

4. API-интерфейсы Windows, с которыми я динамически связываюсь, используют stdcall, поэтому я не могу их изменить. Вот как я хочу это использовать: godbolt.org/z/X9SLWX Я сделал что-то не так?

5. Работает с правильными параметрами: Demo . Поскольку я использовал ссылки для пересылки, NULL это 0 так int (тогда как указатель ожидается для некоторых параметров). специализация может позволить имитировать std::function поведение, но с дополнительными копиями / перемещениями

Ответ №2:

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

 template <typename T>
class CustomWrapper;

template <typename Result, typename ...Args>
class CustomWrapper<Result(Args...)>
{
public:
    template <typename ...SomeArgs>
    CustomWrapper(SomeArgsamp;amp;... args)
     : func(std::forward<SomeArgs>(args)...) 
    {
    }

    CustomWrapper(const CustomWrapperamp;) = default;
    CustomWrapper(CustomWrapperamp;amp;) = default;

    auto operator()(Argsamp;amp;... args)
    {
        return func(std::forward<Args>(args)...);
    }

private:
    std::function<Result(Args...)> func;
};

void foo()
{
    CustomWrapper<void(int, int)> func([](int x1, int x2) {});
    func(1, 2);
}
  

Я не реализовал все методы, но их довольно легко добавить, используя это в качестве примера.

Но также я хочу упомянуть, что если вы вызываете std::function s очень часто — лучшая идея — избавиться от std::function себя, чтобы повысить производительность. Рассмотрите возможность перехода на функциональные объекты, если это возможно в вашем случае.

Комментарии:

1. Будьте осторожны с конструктором шаблонов, CustomWrapper(CustomWrapperamp;) он будет соответствовать вашему шаблону, что приведет к недопустимому коду.

2. @Jarod42, да, вы правы, конструктор копирования и перемещения должен быть реализован отдельно

3. Я имел в виду, что конструктор шаблонов должен быть защищен, или вы должны предоставить все варианты конструктора копирования / перемещения (обычные C(const Camp;); C(Camp;amp;) , но также C(Camp;); C(const Camp;amp;) (и я игнорирую volatile случаи).

4. Я только что попробовал это и не смог скомпилировать динамический импорт: godbolt.org/z/vNo4zv — Вероятно, один из конструкторов отсутствует, но я пока слаб, чтобы найти, что именно 🙂

5. @Unc3nZureD, похоже, проблема связана с __stdcall . std::function в стандартной библиотеке MSVS каким-то образом сохраняет соглашение о вызовах, в то время как моя реализация — нет

Ответ №3:

Вы хотите, чтобы он постоянно проверялся на наличие статистики или только один раз перед отправкой вашей программы? Хотя бы один раз я бы рекомендовал использовать инструменты профилирования / покрытия, чтобы узнать, сколько раз на функцию ссылаются.

Если вам нужна постоянная статистика, вам нужно посмотреть на интерфейс std::function (https://en.cppreference.com/w/cpp/utility/functional/function ) и создать для этого новую реализацию — на самом деле это не так уж сложно реализовать.

Комментарии:

1. Для какой-то специальной сборки я хочу постоянно следить за ней 🙂 Таким образом, переписывание кажется мне единственным разумным решением: (