C++, Maybe

Dec. 23rd, 2012 08:46 pm
madf: (Default)
[personal profile] madf
Колись, якщо я не помиляюсь, [livejournal.com profile] ivan_gandhi писав що методи get це зло. На жаль, я того посту знайти не можу. Пам’ятаю тільки що у коментарях хтось як альтернативу запропонував методи with. Я тоді не звернув увагу.

А два тижні тому у п’ятницю їхав з роботи і надумав собі що це ж класна штука насправді! От уявіть собі вказівник: він може на когось указувати, а може і не указувати. Така його природа. І коли він на когось указує — з ним можна працювати. А коли не указує — не можна. І треба постійно втикати if (foo != nullptr) і подібні штуки.
Ну, raw-pointer це взагалі штука небезпечна. У пристойному суспільстві треба його використання добряче обґрунтувати, а то і канделябром по пальцям отримати не довго. Зараз модно замість raw-pointers використовувати smart-pointers. Про це сам Stroustrup каже.
Так от, „розумні“ вказівники. Оператор розіменування (ідіотська назва, як на мене) чи „стрілка“ — це і є ті самі „геттери“. В чому їхня проблема? Та у тому що коли вказівник пустий то невідомо що робити з тим геттером. Ось три погані варіанти:

  • undefined behaviour;

  • exception;

  • default value.


Чим поганий перший, я думаю, зрозуміло без додаткових коментарів. Ну серйозно, кому сподобається софт з undefined behaviour всередині?
Exception поганий тим що він не є частиною типу функції у C++. Його розповсюдження дуже важко контролювати. Зараз, кажуть, з noexcept стало краще, але я ще його толком не пробував. А що буває з неловленим exception знають усі хто їх хоч раз використовував. Terminate, жахливий і неминучий.
Default value. Цей підхід поганий тим що не у всіх типів буває default value.
На відміну від цих „тупих відмазок“, як мені здається, with вирішує проблему. Дивіться, метод with приймає на вхід функцію у яку треба передати „внутрішнє“ значення. Я маю на увазі, значення на яке вказівник указує. Нема значення — не передаємо. Якщо результат функції void то взагалі ніяких проблем. Якщо тип то тут уже складніше — у нас не буває вказівників на r-value.
Подібний підхід можна застосовувати для масивів щоб примусово перевірити індекс. І для асоціативних масивів. Може, ще для чогось.
Щоб перевірити концепт я вирішив „потренуватись на кішках“ — реалізувати Maybe. Перша спроба була невдала. Але тут з усіх сторін посипались статті і пости про те як можна union і placement new використати для всяких різних фантастичних штук. Ну, там, optional<t> і все таке. Кажуть, цей тип буде у C++14. Та і Сам Александреску там писав про роботу з помилками і тип Either на базі цього трюку. Воістину — свідомість змінює реальність!
Що ж, я не довго вагався і швиденько накалякав маленький PoC. Enjoy:
#include <cmath>

#include <iostream>
#include <utility>
#include <type_traits>

template <typename T>
class Maybe {
public:
typedef T value_type;

template <typename F>
auto with(const F& func) -> Maybe<decltype(func(std::declval<T>()))>
{
if (m_just)
return Maybe<decltype(func(m_value))>::just(func(m_value));
return Maybe<decltype(func(m_value))>::nothing();
}

template <typename VT>
void with(void (&func) (VT))
{
if (m_just)
func(m_value);
}

template <typename VT>
static Maybe<T> just(VT&& v) { return Maybe<T>(std::forward<VT>(v)); }

static Maybe<T> nothing() { return Maybe<T>(); }

private:
union {
T m_value;
bool m_placeholder;
};
bool m_just;

template <typename VT>
Maybe(VT&& v)
: m_value(std::forward<VT>(v)),
m_just(true)
{}

Maybe()
: m_placeholder(false),
m_just(false)
{}
};

void print(int v)
{
std::cout << "v = " << v << std::endl;
}

int main()
{
auto a = Maybe<int>::nothing();
auto b = Maybe<int>::just(10);

auto a1 = a.with(sqrt);
auto b1 = b.with(sqrt);

a1.with(print);
b1.with(print);

return 0;
}
_Winnie C++ Colorizer

Я хочу звернути вашу увагу на те що b1 має тип Maybe<double>, але без проблем працює з print що приймає на вхід int.
Конструювання трошки коряве. Тип можна було б виводити автоматично із аргументу, тіпа make_just(123). Але я хотів зробити можливим неявне конструювання: Maybe<Foo>::just(10). І тут вивести тип уже не вийде.
Крім того стиль починає нагадувати CPS, що теж не додає привабливості.
Цікаво ще те що with це, фактично, map + apply. Можна було б залишити тільки map і повертати std::function чи, там, лямбду і погратись з функторами. А можна було б замінити with на operator>> і oparetor>>= і погратись у монади.
Не знаю, не знаю...
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

madf: (Default)
madf

April 2018

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 26th, 2025 08:42 pm
Powered by Dreamwidth Studios