Как бы реализовать замыкания 2
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 14:32
Оценка:
В продолжение Как бы реализовать замыкания?
Автор: remark
Дата: 17.07.09

В крации, необходимо реализовать замыкания, так что бы:
— неправильные типы аргументов детектируются в месте создания замыкания
— автоматически определяется наиболее адекватный способ как хранить аргумент, т.е. не надо ref/cref как в boost
— отсутствует лишняя работа во время выполнения
— кол-во шаблонов N, а не K^N
— необходима поддержка "опций" для аргументов, типа pass_by_pointer(x), delete_after_use(х) и т.д.

Судя по всему реализовать чисто на шаблонах это не получится. Поэтому сейчас я остановился на следующей технике.
Тип аргумента, передаваемый пользователем, (х, pass_by_pointer(x), delete_after_use(х) и т.д.) определяется в ран-тайм. Для этого с каждым аргументом дополнительно передаётся unsigned type = 0/1/2, который кодирует тип аргумента (х/pass_by_pointer(x)/delete_after_use(х)). Далее сразу после получения всех этих ран-тайм type, они преобразуются в параметры шаблона. В данном конкретном случае с помощью рекурсивных шаблонных функций, но в принципе можно применить и switch, и "лесенку if'ов". Идея в том, что преобразование это прекрасно инлайниться и устраняется, т.к. в реальности-то типы у нас известны на этапе компиляции. Т.е. фактически мы как бы компайл-тайм информацию временно делаем ран-тайм (по-крайней мере формально), а потом обратно "возвращаем" в ранг компайл-тайм информации. При этом при релиз компиляции мы не получаем никаких дополнительных накладных расходов, но зато существенно расширяем возможности шаблонов (как бы).

В общем в итоге я получил работающий прототип, который отвечает всем указанным требованиям.
Вот небольшой пример использования. Тут spawn() сразу же напрямую вызывает переданную функцию, Х — тип пустышка. Как видно, код получился оптимальный.
void bar(int x, X* y);
int main()
{
    spawn(bar, 5, delete_after_use(new X));
00401071  push        1    
00401073  call        operator new (401384h) 
00401078  mov         esi,eax 
0040107A  push        esi  
0040107B  push        5    
0040107D  call        bar (401000h) 
00401082  push        esi  
00401083  call        dword ptr [__imp__free (4020D0h)] 
00401089  add         esp,10h 
}


Ниже я приведу полную реализацию, но она достаточно запутанная, и была сделана с единственной целью быть proof-of-concept. Определенно её можно сильно причесать, но это уже дело техники. Поэтому прошу тут сильно не придираться. Особое внимание обратите на класс wrapper, который кодирует тип в виде ран-тайм значения; и на класс helper, который производит обратное преобразование в компайл-тайм значение. Всё остальное — это туева хуча различных хелперов и обёрток.
Код рабочий, можно его скомпилировать и поиграться.

template<typename T>
struct param_traits
{
    typedef T param;
};

template<typename T>
struct pass_by_pointer_t
{
    typename param_traits<T>::param obj;
};

template<typename T>
pass_by_pointer_t<T> pass_by_pointer(typename param_traits<T>::param obj)
{
    pass_by_pointer_t<T> res = {obj};
    return res;
}

template<typename T>
struct delete_after_use_t
{
    typename param_traits<T>::param obj;
};

template<typename T>
void delete_after_use(T obj)
{
}

template<typename T>
delete_after_use_t<T*> delete_after_use(T* obj)
{
    delete_after_use_t<T*> res = {obj};
    return res;
}

template<typename T>
struct null_option_t
{
    static void after_execute(typename param_traits<T>::param) {}
};

template<typename T>
struct pass_by_pointer_option_t
{
    static void after_execute(typename param_traits<T>::param obj)
    {
    }
};

template<typename T>
struct delete_after_use_option_t
{
    static void after_execute(typename param_traits<T>::param obj)
    {
    }
};

template<typename T>
struct delete_after_use_option_t<T*>
{
    static void after_execute(typename param_traits<T*>::param obj)
    {
        obj->~T();
        free(obj);
    }
};

template<typename T>
struct wrapper
{
    unsigned type;
    typename param_traits<T>::param v;

    wrapper(typename param_traits<T>::param v)
        : type (0)
        , v (v)
    {}

    wrapper(pass_by_pointer_t<T> v)
        : type (1)
        , v (v.obj)
    {}

    wrapper(delete_after_use_t<T> v)
        : type (2)
        , v (v.obj)
    {}
};

template<size_t idx>
struct get_arg_type;

template<> struct get_arg_type<1>
{
    static unsigned type(unsigned type1, unsigned type2, unsigned type3)
    {
        return type1;
    }
};

template<> struct get_arg_type<2>
{
    static unsigned type(unsigned type1, unsigned type2, unsigned type3)
    {
        return type2;
    }
};

template<> struct get_arg_type<3>
{
    static unsigned type(unsigned type1, unsigned type2, unsigned type3)
    {
        return type3;
    }
};

template<bool, typename F, typename T>
struct if_t
{
    typedef F type;
};

template<typename F, typename T>
struct if_t<true, F, T>
{
    typedef T type;
};

template<size_t total_arg_count, size_t current_arg_idx, typename A1, typename A2 = void*, typename A3 = void*, typename W1 = void, typename W2 = void, typename W3 = void>
struct helper
{
    __forceinline
    static void exec(void* f, unsigned type1, unsigned type2, unsigned type3, typename param_traits<A1>::param a1, typename param_traits<A2>::param a2, typename param_traits<A3>::param a3)
    {
        unsigned type = get_arg_type<current_arg_idx>::type(type1, type2, type3);
        if (type == 0)
            helper<total_arg_count, current_arg_idx + 1, A1, A2, A3,
                if_t<current_arg_idx == 1, W1, null_option_t<A1> >::type,
                if_t<current_arg_idx == 2, W2, null_option_t<A2> >::type,
                if_t<current_arg_idx == 3, W3, null_option_t<A3> >::type>
                    ::exec(f, type1, type2, type3, a1, a2, a3);
        else if (type == 1)
            helper<total_arg_count, current_arg_idx + 1, A1, A2, A3,
                if_t<current_arg_idx == 1, W1, pass_by_pointer_option_t<A1> >::type,
                if_t<current_arg_idx == 2, W2, pass_by_pointer_option_t<A2> >::type,
                if_t<current_arg_idx == 3, W3, pass_by_pointer_option_t<A3> >::type>
                    ::exec(f, type1, type2, type3, a1, a2, a3);
        else if (type == 2)
            helper<total_arg_count, current_arg_idx + 1, A1, A2, A3,
                if_t<current_arg_idx == 1, W1, delete_after_use_option_t<A1> >::type,
                if_t<current_arg_idx == 2, W2, delete_after_use_option_t<A2> >::type,
                if_t<current_arg_idx == 3, W3, delete_after_use_option_t<A3> >::type>
                    ::exec(f, type1, type2, type3, a1, a2, a3);
    }
};

template<typename A1, typename A2, typename A3, typename W1, typename W2, typename W3>
struct helper<1, 2, A1, A2, A3, W1, W2, W3>
{
    __forceinline
    static void exec(void* f, unsigned type1, unsigned type2, unsigned type3, typename param_traits<A1>::param a1, typename param_traits<A2>::param a2, typename param_traits<A3>::param a3)
    {
        typedef void(*real_t)(A1);
        real_t real_f = (real_t)f;
        real_f(a1);
        W1::after_execute(a1);
    }
};

template<typename A1, typename A2, typename A3, typename W1, typename W2, typename W3>
struct helper<2, 3, A1, A2, A3, W1, W2, W3>
{
    __forceinline
    static void exec(void* f, unsigned type1, unsigned type2, unsigned type3, typename param_traits<A1>::param a1, typename param_traits<A2>::param a2, typename param_traits<A3>::param a3)
    {
        typedef void(*real_t)(A1, A2);
        real_t real_f = (real_t)f;
        real_f(a1, a2);
        W1::after_execute(a1);
        W2::after_execute(a2);
    }
};

template<typename T>
struct id
{
    typedef T type;
};

template<typename A1, typename A2>
__forceinline
void spawn(void(*f)(A1, A2), typename id<wrapper<A1> >::type a1, typename id<wrapper<A2> >::type a2)
{
    helper<2, 1, A1, A2>::exec(f, a1.type, a2.type, 0, a1.v, a2.v, 0);
}

void foo(int x, std::string const& y)
{
    std::cout << "foo(" << x << ", " << y << ")" << std::endl;
}

struct X
{
    X()
    {
#ifdef _DEBUG
        std::cout << __FUNCSIG__ << std::endl;
#endif
    }

    ~X()
    {
#ifdef _DEBUG
        std::cout << __FUNCSIG__ << std::endl;
#endif
    }
};

__declspec(noinline)
void bar(int x, X* y)
{
    std::cout << "bar(" << x << ", " << "X" << ")" << std::endl;
}

int main()
{
    //std::string str = "hello";
    //spawn(foo, 5, str);
    spawn(bar, 5, delete_after_use(new X));
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.