Теперь можно задуматься над расширением нашей библиотеки - количество параметров в вызываемой функции (функторе/методе класса) не обязательно равно четырем.
Чтобы решить эту проблему я предлагаю воспользоваться кодогенератором.
Причин поступить именно так, помимо моей личной лени, достаточно много:
Эти требования сразу же решают судьбу списков типов в стиле Александреску и и BOOST_PREPROCESSOR - мы хотим сделать сразу работающий вариант, который не требуется каждый раз долго и мучительно компилировать, для больших проектов такие "гениальные" модули могут замедлять сборку очень серьезно...
В качестве инструмента возьмем Perl - это все еще неплохое средство для работы с текстами, несмотря на тотальное засилие Python :-)
Немного перетасуем наши исходные коды - все разобъем условно на три части:
Каждой из частей мы сопоставим одну подпрограмму Perl (потом вторую подпрограмму мы вызовем потом столько раз, сколько нужно иметь аргументов).
Следующий шаг - выделение паттернов, которые будут общими для каждой реализации bind_obj_{n}:
Для каждого найденного паттерна организуем собственную переменную, используя конструкцию:
my $ var = map { "xxx$_" } (1..$arg_count)Для остального вывода удобнее всего употребить ряд конструкций "document here" вида
print<<EOT;
my string1 $arg_count
my string2
EOT
Все деликатные внутренние моменты реализуем с помощью обычного оператора for () {}.
Да, use strict использовать обязательно, иначе отлаживаться можно до второго пришествия, а мы используем генератор чтобы экономить время...
Собственно программа укладывается в несколько строчек:
print<<EOT;
my $ARGS=9;
header $ARGS;
for (1..$ARGS) {
   body $_;
}
footer $ARGS;
Обращаю внимание, body на самом деле должна вызываться, начиная с нулевого количества параметров, после чего все сразу перестает компилироваться, какое-то время уходит на адаптацию кода к отсутствию аргументов (это резко усиливает бардачность), но оно того стоит...
Эти 400 строк высоконечитабельного кода, которые дают нам нужный результат (ценителей Perl прошу смотреть в другую сторону) можно взять здесь, а полученный заголовок здесь. Полный проект MSVC2003, полный проект можно взять здесь.
Тестовый набор, который отработал (даже несколько более широкий, чем изначально изначально планировалось):
int f4(int a, int b, int c, int d)
{
    return a + b + c + d;
}
int f3(int a, int b, int c)
{
    return a + b + c;
}
int f2(int a, int b)
{
    return a + b;
}
int f1(int a)
{
    return a + 1;
}
int f0()
{
    return 1;
}
struct X
{
    typedef int result_type;
    int f1(int a)
    {
        return a+1;
    }
    int f0()
    {
        return 1;
    }
    int f(int a1, int a2, int a3)
    { 
        return a1 + a2 + a3;
    }
};
struct S
{
    std::string s;
    int f()
    {
    }
};
int main(int argc, char* argv[])
{
    X x;
    using namespace mbind;
    std::cout
         << bind(&f4, 1, 2, 3, _1)(4) << "\n"
         << bind(&f4, 1, 2, 3, 4)() << "\n"
         << bind(&f4, 1, _1, _2, _3)(2, 3, 4) << "\n"
         << bind(&X::f, &x, _1, _2, 3)(2, 3) << "\n"
         << bind(&f1, _1)(2) << "\n"
         << bind(&X::f1, &x, _1)(2) << "\n"
         << bind(&f3, 1, _1, _2)(2, 3) << "\n"
         << bind(&X::f0, &x)() << "\n"
         << bind(&f0)() << "\n"
         << bind(std::plus<int>(), bind(&f1, _1), _1)(2) << "\n"
         << bind(std::plus<int>(), bind(&f2, _1, 2), _1)(1) << "\n"
         << bind(std::plus<int>(), bind(&f1, _1), _2)(1, 2) << "\n"
         << bind(std::plus<int>(), bind(&f1, _1), _2)(1, 2) << std::endl;
    S s;
    s.s = "data";
    std::cout
        << bind(&S::s, s)() << "\n"
        << bind(&S::s, _1)(s) << "\n"
        << bind(&S::s, &s)() << std::endl;
    std::string s1("1"), s2("2");
    std::cout << bind(std::plus<std::string>(), _1, _2)(s1, s2) << std::endl;
}Что можно сказать относительно полученного результата?
Единственный случай, когда их можно терпеть в коде - использование какого-нибудь олдскульного API ;-)
А так, почти ничего особо хорошего:
00404854  lea         edx,[esp+110h] 
0040485B  mov         dword ptr [ecx+18h],esi 
0040485E  mov         dword ptr [ecx+14h],ebx 
00404861  push        edx  
00404862  mov         byte ptr [esp+13Ch],1 
0040486A  mov         byte ptr [ecx+4],bl 
0040486D  call        std::basic_string<char,std::char_traits<char>,std::allocator>char> >::assign (402010h) 
00404872  lea         eax,[esp+78h] 
00404876  push        eax  
00404877  lea         ecx,[esp+3Ch] 
0040487B  mov         dword ptr [esp+3Ch],ebx 
0040487F  call        mbind::bind_obj_1<std::basic_string<char,std::char_traits<char>,std::allocator<char> > S::*,mbind::arg<1>,1>::operator()<S>
 (404210h)Код с оригинальным boost выглядит несколько иначе:
004034EC  push        ebx  
004034ED  mov         dword ptr [esi+18h],0Fh 
004034F4  mov         dword ptr [esi+14h],ebx 
004034F7  push        eax  
004034F8  mov         ecx,esi 
004034FA  mov         byte ptr [esi+4],bl 
004034FD  call        std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign (402010h) 
00403502  cmp         dword ptr [esp+24h],10h 
00403507  jb          std::operator+<char,std::char_traits<char>,std::allocator<char> >+86h (403516h) 
00403509  mov         edx,dword ptr [esp+10h] 
0040350D  push        edx  
0040350E  call        operator delete (406B5Dh) 
00403513  add         esp,4Как только мы устраним все вопросы, объем кода устремится к оригиналу, а его понятность упадет ниже плинтуса...
Вывод прост - как игра ума это все забавно, в Production коде же желательно использовать проверенный boost::bind, или его аналог из lambda :-)
BTW, функторы в Loki тоже ведут себя не самым лучшим образом, что народ подтверждает :-)
 
Комментариев нет:
Отправить комментарий