madf: (Default)
[personal profile] madf
На роботі дістали проблеми з валідацією вхідних даних. Мало того що код що займається валідацією застарів ще у 1998-му, так він ще і не завжди працює. Скажімо, для GUI працює, а для batch - ні. Ну і там все в стилі ранніх C - дикі пляски з void* і ручна типізація.
Я колись займався сайтами, і був у мене сайтовий рушій. Рушій не мій, його свого часу написав один класний чувак. І найбільше мені там подобалось те як влаштована валідація і препроцесинг вхідних даних форм.
Коли я робив систему моніторингу для атомщиків, я спробував втілити ідеї із того сайтового рушія у C++. Вийшло не дуже.
А тут я взявся за справу серйозно, і от що отримав:

#include <iostream>
#include <string>
#include <functional>

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/trim.hpp>

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

CanFail(const CanFail<T> &) = default;
CanFail<T> & operator=(const CanFail<T> &) = default;

const T & value() const { return m_value; }
bool failed() const { return m_failed; }
const std::string & error() const { return m_error; }

static CanFail<T> Ok(const T & value) { return CanFail<T>(value); }
static CanFail<T> Fail(const std::string & error = "") { CanFail<T> temp; temp.m_error = error; return temp; }

private:
T m_value;
bool m_failed;
std::string m_error;

CanFail(const T & value) : m_value(value), m_failed(false) {}
CanFail() : m_value(), m_failed(true) {}
};

template <typename C, typename B, typename A>
inline
std::function<C(A)> compose(std::function<C(typename B::value_type)> f1, std::function<B(A)> f2)
{
return [f1, f2](A a) -> C {
auto temp(f2(a));
if (temp.failed())
return C::Fail(temp.error());
return f1(temp.value());
};
}

template <typename T>
inline
std::function<CanFail<T>(T)> id(){ return [](const T & value) { return CanFail<T>::Ok(value); }; }

template <typename A, typename B, typename C>
inline
std::function<C(A)> operator<<(std::function<C(typename B::value_type)> f1, std::function<B(A)> f2)
{
return compose(f1, f2);
}

template <typename A, typename B, typename C>
inline
std::function<C(A)> operator<<(std::function<C(typename B::value_type)> f1, B (&f2) (A))
{
return compose(f1, makeFunction(f2));
}

template <typename R, typename A>
inline
std::function<R(A)> makeFunction(R(&f)(A))
{
return std::function<R(A)>(f);
}

inline
CanFail<std::string> trim(const std::string & value)
{
return CanFail<std::string>::Ok(boost::algorithm::trim_copy(value));
}

inline
CanFail<std::string> notEmpty(const std::string & value)
{
if (value.empty())
return CanFail<std::string>::Fail("Empty value");
return CanFail<std::string>::Ok(value);
}

template <typename T>
inline
CanFail<T> cast(const std::string & value)
{
try {
return CanFail<T>::Ok(boost::lexical_cast<T>(value));
}
catch (const boost::bad_lexical_cast & e)
{
return CanFail<T>::Fail(e.what());
}
}

int main()
{
//auto validate = compose(makeFunction(toInt), compose(makeFunction(notEmpty), makeFunction(trim)));
//auto validate = makeFunction(cast<int>) << makeFunction(notEmpty) << makeFunction(trim);
auto validate = id<int>() << cast<int> << notEmpty << trim;
auto res = validate(" 123 ");
if (res.failed()) {
std::cout << "Validation failed: " << res.error() << std::endl;
} else {
std::cout << "Validation succeed: " << res.value() << std::endl;
}
return 0;
}
_Winnie C++ Colorizer


Приклади використання у main(). Початкова реалізація вимагала явного використання compose. Потім я додав overloaded operator <<, але ця реалізація вимагала ручного приведення типів (див. makeFunction). А потім я додав id() і все стало на свої місця!
Звісно, це тільки proof-of-concept. Зацініть монадичну композицію і identity function!
І да, CanFail<T> можна замінити на exception і використовувати звичайну композицію: "Expressive C++: Fun With Function Composition", "Function composition".

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 Jan. 7th, 2026 06:45 pm
Powered by Dreamwidth Studios