Пекельне метапрограмування - 2
Jul. 7th, 2012 05:55 pmНа роботі дістали проблеми з валідацією вхідних даних. Мало того що код що займається валідацією застарів ще у 1998-му, так він ще і не завжди працює. Скажімо, для GUI працює, а для batch - ні. Ну і там все в стилі ранніх C - дикі пляски з void* і ручна типізація.
Я колись займався сайтами, і був у мене сайтовий рушій. Рушій не мій, його свого часу написав один класний чувак. І найбільше мені там подобалось те як влаштована валідація і препроцесинг вхідних даних форм.
Коли я робив систему моніторингу для атомщиків, я спробував втілити ідеї із того сайтового рушія у C++. Вийшло не дуже.
А тут я взявся за справу серйозно, і от що отримав:
Приклади використання у main(). Початкова реалізація вимагала явного використання compose. Потім я додав overloaded operator <<, але ця реалізація вимагала ручного приведення типів (див. makeFunction). А потім я додав id() і все стало на свої місця!
Звісно, це тільки proof-of-concept. Зацініть монадичну композицію і identity function!
І да, CanFail<T> можна замінити на exception і використовувати звичайну композицію: "Expressive C++: Fun With Function Composition", "Function composition".
Я колись займався сайтами, і був у мене сайтовий рушій. Рушій не мій, його свого часу написав один класний чувак. І найбільше мені там подобалось те як влаштована валідація і препроцесинг вхідних даних форм.
Коли я робив систему моніторингу для атомщиків, я спробував втілити ідеї із того сайтового рушія у 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".