Re: Этюд: значения по умолчанию
От: Alexey F  
Дата: 27.10.09 20:36
Оценка: 70 (3)
Здравствуйте, Кодт, Вы писали:

К>Предлагаю этюд для С++гуру: сделать библиотечное решение данной проблемы.

[...skip...]
К>Как обобщить на произвольные типы? И как подсовывать по дефолту значения времени исполнения?

Не гуру, но можно попробовать?

Если сделать как один класс, будет выглядеть довольно громоздко, что-то вроде:
foo ( value< char, value_int<char, 'c'> > x, value< double, value_func<double, Functor> > y ... )

(можно спрятать лишний раз указываемый тип, но, кажись, не избавимся от value< ..., value_... <...> >).

Следовательно, разделяем классы:
...
#include <cmath>


enum placeholder { _ }; // Нехорошо, наверное (:xz:), нижним подчёркиванием, но оставим так для чистоты в коде примера.


// Целые значения:
// (оставил почти без изменений)
template<class T, T V = T()>
struct i_param // integer parameter
{
    i_param () : value(V) {}
    i_param (placeholder) : value(V) {}
    template<class U> i_param (U u) : value(u) {}

    T get() const { return value; }
    operator T() const { return value; }

    T value; // оставим открытым на случай, если надо будет адрес взять, например.
};


// Дробные значения:
// Вспомогательный код:
namespace aux {
    // long/long long по вкусу
    typedef long long integral_part;
    typedef unsigned long long rational_part;


    template<rational_part number>
    struct digits_in_number {
        enum { result = 1 + digits_in_number<number / 10>::result };
    };


    template<>
    struct digits_in_number<0> {
        enum { result = 0 };
    };
}

template<class T, aux::integral_part Integral, aux::rational_part Rational>
struct f_param { // float parameter
    f_param () : value ( makeValue () ) {}
    f_param ( placeholder ) : value ( makeValue () ) {}

    template<class U>
    f_param ( U const& other )
        : value ( other ) {}

    T get () const {
        return value;
    }

    operator T () const {
        return value;
    }

    T value;

private:
    inline T makeValue () const {
        return
            ( std::abs ( static_cast<T> ( Integral ) ) + ( Rational * std::pow ( 10, -aux::digits_in_number<Rational>::result ) ) )
            * ( Integral >= 0 ? 1 : -1 )
        ;
    }
};

// Ну и, наконец, произвольные значения (в том числе и времени выполнения), которые получаются через функтор
// (только вот параметры ему не передашь):
template<class T, class Functor>
struct func_param { // functor parameter
    func_param () : value ( Functor ()() ) {}
    func_param ( placeholder ) : value ( Functor ()() ) {}

    template<class U>
    func_param ( U const& other )
        : value ( other ) {}


    T const& get () const {
        return value;
    }


    operator T const& () const {
        return value;
    }

    T value;
};


Пример использования:
...

struct SimpleGetter {
    std::string operator ()() const {
        return "test";
    }
};


void foo (
    i_param<char, 'c'> x,
    i_param<int, 7> y,
    i_param<unsigned, 9> z,
    f_param<double, 3,14> a, // запятая, а не точка!
    func_param<std::string, SimpleGetter> b // std::string const& в такой форме уже не сделаешь :)
)
{
    cout << x.get() << " " << y.get() << " " << z.get() << ' ' << a.get () << ' ' << b.get () << endl;
}

int main()
{
    foo('a', _, _,  _,       _ );
    foo( _,  2, _,  _,       "test 2" );
    foo( _,  _, -3, 3.14159, _ );
}


Результат:

a 7 9 3.14 test
c 2 9 3.14 test 2
c 7 4294967293 3.14159 test


P.S. У кого сомнения насчёт pow, abs в f_param то gcc 4.4.1 (да и используемый мной более старый, если не изменяет память) в -O2 их оптимизирует до подсовывания константы:
Было:
...

int main () {
    printf ( "%f", f_param<double, 3,14159> ().get () );
}

Стало:
00401356   |. C74424 04 6E861BF0        MOV DWORD PTR SS:[ESP+4],F01B866E                     ; |
0040135E   |. C74424 08 F9210940        MOV DWORD PTR SS:[ESP+8],400921F9                     ; |
00401366   |. C70424 24304700           MOV DWORD PTR SS:[ESP],default_.00473024              ; |ASCII "%f"
0040136D   |. E8 2EFD0100               CALL <JMP.&msvcrt.printf>                             ; \printf


Листинг получился один-в-один с:
printf ( "%f", 3.14159 );

 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.