Шаблонная функция, принимающая вызываемые функторы с параметрами X

Я пишу размещенную программу на C++, которая запускает написанный пользователем C-код, скомпилированный на лету. Крайне важно, чтобы некоторые типичные исключения перехватывались из C-кода и обрабатывались/игнорировались. Для этого я вызываю код C из структурированного блока обработки исключений. Из-за характера и семантики этого блока (и откуда он вызывается) я отделил фактический вызов от его собственной функции:

    template <typename ret_type, class func>
        static ret_type Cstate::RunProtectedCode(func function) {
            ret_type ret = 0;
            __try {
                ret = function();
            }
            __except(ExceptionHandler(GetExceptionCode(), ExceptionStatus::CSubsystem)) {
                fprintf(stderr, "First chance exception in C-code.\n");
            }
            return ret;
        }

Что хорошо работает, как и должно быть:

        RunProtectedCode<int>(entry);

Но возможно ли сформировать это так, чтобы я мог вызывать функции с переменным количеством параметров - возможно, с помощью некоторого использования экзотических функторов (единственное требование, очевидно, заключается в том, что у него не может быть деструктора)? Я использую MSVC++ 2010.


person Shaggi    schedule 16.05.2013    source источник
comment
Есть вариативные шаблоны, но я боюсь, что они не поддерживаются в VS 2010.   -  person chris    schedule 16.05.2013
comment
Не против обновиться до VS2012, если это можно сделать там. Можете ли вы сделать пример кода?   -  person Shaggi    schedule 16.05.2013
comment
К сожалению, VS2012 также не поддерживает вариативные шаблоны.   -  person Xeo    schedule 16.05.2013
comment
@Xeo, совершенно верно, но пока есть CTP, если это допустимо.   -  person chris    schedule 16.05.2013
comment
@chris: Жаль, что CTP содержит то, что мы называем ошибками :/   -  person Xeo    schedule 16.05.2013
comment
В С++ 11 (который в данном случае VS2012 достаточно хорошо аппроксимирует) это может быть легко, просто передайте лямбду [&](){foo(arg1, arg2, arg3);} в RunProtectedCode вместо функции, которую вы обычно передаете, и вызов foo выполняется, когда лямбда оценивается .   -  person Yakk - Adam Nevraumont    schedule 16.05.2013
comment
Уххх, это лямбда-решение идеально, никаких изменений в коде или IDE не требуется :) Большое спасибо, если вы хотите поставить галочку, вы можете написать ее в ответе!   -  person Shaggi    schedule 16.05.2013


Ответы (2)


Если вы можете использовать С++ 11, вы можете добиться этого с помощью переменных шаблонов.

template <typename ret_type, class func, typename... Args>
    static ret_type Cstate::RunProtectedCode(func function, Args&&... args) {
        ret_type ret = 0;
        __try {
            ret = function(std::forward<Args>(args)...);
        }
        __except(ExceptionHandler(GetExceptionCode(), ExceptionStatus::CSubsystem)) {
            fprintf(stderr, "First chance exception in C-code.\n");
        }
        return ret;
    }

И вы можете назвать это как

RunProtectedCode<int>(entry2, 1, 2);
RunProtectedCode<int>(entry3, 1, "a", 3);

Вы можете упростить его (что-то вроде), используя вместо этого std::function.

template <class func, typename... Args>
    static 
    typename func::result_type Cstate::RunProtectedCode(func function, Args&&... args) {
        typename func::result_type ret = typename func::result_type();
        __try {
            ret = function(std::forward<Args>(args)...);
        }
        __except(ExceptionHandler(GetExceptionCode(), ExceptionStatus::CSubsystem)) {
            fprintf(stderr, "First chance exception in C-code.\n");
        }
        return ret;
    }

И вы можете назвать это как

std::function<int(int,int,int)> entry_f = entry;
RunProtectedCode(entry_f,1,2,3);
person stardust    schedule 16.05.2013
comment
Начиная с C++17 идиоматическим способом вызова вызываемых объектов является использование std::invoke. - person renardesque; 09.09.2019

Вы можете привязать все аргументы к своей функции, сделав ее фактически 0-арным функтором, например. используя std::bind (доступно в VC2010) или boost::bind (я предпочитаю этот, потому что реализация VC содержит неработающие std::cref). Связывание может быть выполнено в перегруженной функции перед переходом к RunProtectedCode, например. что-то вроде этого:

template<typename R>
R(*f)() wrap(R(*f)())
{
    return f;
}

template<typename R, typename A>
boost::function<R(A)> wrap(R(*f)(), A a)
{
    return boost::bind(f, a);
}

template<typename R, typename A1, typename A2>
boost::function<R(A1, A2)> wrap(R(*f)(), A1 a1, A2 a2)
{
    return boost::bind(f, a1, a2);
}
person Andriy Tylychko    schedule 16.05.2013
comment
Также стоит отметить, что в VS2010 std::bind копия конструирует свои аргументы примерно 10 раз. boost::bind и std::bind в VS2012 также ведут себя лучше в этом отношении. - person Sean Cline; 16.05.2013