C++: Функциональная обертка, которая ведет себя точно так же, как сама функция

Я вижу две опции, которые можно использовать с k8s:

  1. Используйте istio управление трафиком и создайте DestinationRule. В настоящее время он поддерживает три режима балансировки нагрузки:

    • Round robin
    • Случайный
    • Взвешенный наименьший запрос

      apiVersion: networking.istio.io/v1alpha3
      kind: DestinationRule
      ...
      spec:
        ...
        subsets:
        - name: test
          ...
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
      
  2. Использовать lb_type в прокси посланника с после на k8s. Более подробная информация о после находится в https://www.getambassador.io .

15
задан Frank 18 May 2009 в 19:53
поделиться

9 ответов

Вот простой способ переноса функций.

template<typename T>
class Functor {
  T f;
public:
  Functor(T t){
      f = t;
  }
  T& operator()(){
    return f;
  }
};


int add(int a, int b)
{
  return a+b;
}

void testing()
{
  Functor<int (*)(int, int)> f(add);
  cout << f()(2,3);
}
9
ответ дан 1 December 2019 в 01:24
поделиться

Если вы хочет создать универсальный класс, который может оборачивать и вызывать произвольную функцию. В этом случае вам придется заставить функтор (оператор ()) возвращать double и принимать int в качестве параметра. Затем вы создали семейство классов, которые могут вызывать все функции с той же сигнатурой. Как только вы захотите добавить больше типов функций, вам понадобится больше функторов этой сигнатуры, например

MyClass goo(double a, double b)
{
   // ..
}

template<class Function>
class Timer {

public:

  Timer(Function& fct)
  : fct_(fct) {}

  MyClass operator()(double a, double b){

  }

};

EDIT: некоторые орфографические ошибки

1
ответ дан 1 December 2019 в 01:24
поделиться

По сути, то, что вы хотите сделать, невозможно в текущем C ++. Для любого числа функций, которые вы хотите обернуть, вам нужно перегрузить на

const reference
non-const reference

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

template<class Function>
class Timer {
    typedef typename boost::function_types
       ::result_type<Function>::type return_type;

public:

  Timer(Function fct)
  : fct_(fct) {}

// macro generating one overload
#define FN(Z, N, D) \
  BOOST_PP_EXPR_IF(N, template<BOOST_PP_ENUM_PARAMS(N, typename T)>) \
  return_type operator()(BOOST_PP_ENUM_BINARY_PARAMS(N, T, const& t)) { \
      /* some stuff here */ \
      fct_(ENUM_PARAMS(N, t)); \
  }

// generate overloads for up to 10 parameters
BOOST_PP_REPEAT(10, FN, ~)
#undef FN

  long GetElapsedTime() { return elapsed_time_; }

private:
  // void() -> void(*)()
  typename boost::decay<Function>::type fct_;
  long elapsed_time_;
};

Обратите внимание, что для возвращаемого типа вы можете использовать библиотеку типов функций boost. Затем

Timer<void(int)> t(&foo);
t(10);

Вы также можете выполнить перегрузку, используя параметры с чистым значением, а затем, если вы хотите передать что-то по ссылке, используйте boost :: ref . На самом деле это довольно распространенный метод, особенно когда такие параметры собираются сохранить (этот метод также используется для boost :: bind ):

// if you want to have reference parameters:
void bar(int &i) { i = 10; }

Timer<void(int&)> f(&bar);
int a; 
f(boost::ref(a)); 
assert(a == 10);

Или вы можете пойти и добавить эти перегрузки как для const, так и для неконстантные версии, как описано выше. Посмотрите Boost.Preprocessor , чтобы узнать, как писать правильные макросы.

Вы должны знать, что все станет сложнее, если вы хотите иметь возможность передавать произвольные вызываемые объекты (не только функции), поскольку вам понадобится способ затем получить их тип результата (это не так просто) . C ++ 1x упростит подобные вещи.

10
ответ дан 1 December 2019 в 01:24
поделиться

Я предполагаю, что вам это нужно для тестовых целей, и вы не собираетесь использовать их в качестве настоящих прокси или декораторов. Таким образом, вам не нужно использовать operator () и вы можете использовать любой другой более-менее удобный метод вызова.

template <typename TFunction>
class TimerWrapper
{
public:
    TimerWrapper(TFunction function, clock_t& elapsedTime):
        call(function),
        startTime_(::clock()),
        elapsedTime_(elapsedTime)
    {
    }

    ~TimerWrapper()
    {
        const clock_t endTime_ = ::clock();
        const clock_t diff = (endTime_ - startTime_);
        elapsedTime_ += diff;
    }

    TFunction call;
private:
    const clock_t startTime_;
    clock_t& elapsedTime_;
};


template <typename TFunction>
TimerWrapper<TFunction> test_time(TFunction function, clock_t& elapsedTime)
{
    return TimerWrapper<TFunction>(function, elapsedTime);
}

Таким образом, чтобы протестировать некоторые из ваших функций, вы должны использовать только функцию test_time , а не прямую TimerWrapper структура

int test1()
{
    std::cout << "test1\n";
    return 0;
}

void test2(int parameter)
{
    std::cout << "test2 with parameter " << parameter << "\n";
}

int main()
{
    clock_t elapsedTime = 0;
    test_time(test1, elapsedTime).call();
    test_time(test2, elapsedTime).call(20);
    double result = test_time(sqrt, elapsedTime).call(9.0);

    std::cout << "result = " << result << std::endl;
    std::cout << elapsedTime << std::endl;

    return 0;
}
6
ответ дан 1 December 2019 в 01:24
поделиться

Мне не совсем понятно, что вы ищете .. Однако для данного примера это просто:

void operator() (int x)
{
   clock_t start_time = ::clock();    // time before calling
   fct_(x);                           // call function
   clock_t end_time = ::clock();      // time when done

   elapsed_time_ += (end_time - start_time) / CLOCKS_PER_SEC;
}

Примечание: Это будет измерять время в секундах. Если вам нужны высокоточные таймеры, вам, вероятно, придется проверить функциональность ОС (например, GetTickCount или QueryPerformanceCounter в Windows).

Если вы хотите иметь универсальную функцию wrapper, вам стоит взглянуть на Boost.Bind , который вам очень поможет.

1
ответ дан 1 December 2019 в 01:24
поделиться

Если ваш компилятор поддерживает макросы с переменным числом аргументов, я бы попробовал следующее:

class Timer {
  Timer();// when created notes start time
  ~ Timer();// when destroyed notes end time, computes elapsed time 
}

#define TIME_MACRO(fn, ...) { Timer t; fn(_VA_ARGS_); } 

Итак, чтобы использовать его, вы бы сделали следующее:

void test_me(int a, float b);

TIME_MACRO(test_me(a,b));

Это сразу же, и вы бы нужно поиграться, чтобы возвращаемые типы работали (я думаю, вам нужно будет добавить имя типа к вызову TIME_MACRO, а затем заставить его генерировать временную переменную).

1
ответ дан 1 December 2019 в 01:24
поделиться

Вот как бы я это сделал, используя указатель на функцию вместо шаблона:

// pointer to a function of the form:   double foo(int x);
typedef double  (*MyFunc) (int);


// your function
double foo (int x) {
  // do something
  return 1.5 * x;
}


class Timer {
 public:

  Timer (MyFunc ptr)
    : m_ptr (ptr)
  { }

  double operator() (int x) {
    return m_ptr (x);
  }

 private:
  MyFunc m_ptr;
};

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

  Timer t(&foo);
  // call function directly
  foo(i);
  // call it through the wrapper
  t(i);
0
ответ дан 1 December 2019 в 01:24
поделиться

В C ++ функции - это первоклассные граждане, вы можете буквально передать функцию как значение.

Поскольку вы хотите, чтобы она принимала int и возвращала двойное:

Timer(double (*pt2Function)(int input)) {...
0
ответ дан 1 December 2019 в 01:24
поделиться

Страуструп продемонстрировал навык обертки функции (предписание) с перегрузкой оператора -> . Ключевая идея: operator -> будет повторно вызываться, пока не встретит собственный тип указателя, поэтому пусть Timer :: operator -> вернет временный объект, а временный объект вернет его указатель. Затем произойдет следующее:

  1. temp obj создан (вызван ctor).
  2. целевая функция вызвана.
  3. temp obj уничтожен (вызван dtor).

И вы можете ввести любой код в ctor и dtor. Как это.

template < class F >
class Holder {
public:
    Holder  (F v) : f(v) { std::cout << "Start!" << std::endl ; }
    ~Holder ()           { std::cout << "Stop!"  << std::endl ; }
    Holder* operator->() { return this ; }
    F f ;
} ;

template < class F >
class Timer {
public:
    Timer ( F v ) : f(v) {}
    Holder<F> operator->() { Holder<F> h(f) ; return h ; }
    F f ;
} ;

int foo ( int a, int b ) { std::cout << "foo()" << std::endl ; }

int main ()
{
    Timer<int(*)(int,int)> timer(foo) ;
    timer->f(1,2) ;
}

Реализация и использование просты.

3
ответ дан 1 December 2019 в 01:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: