decltype(declval<A&>().cbegin(), declval<A&>().cend(), void())
> : true_type {};
+template <class C>
+inline typename enable_if<is_iterable<C>::value, string>::type
+implode(const C & v, string delim=" ") {
+ if (v.begin() == v.end())
+ return string();
+ ostringstream oss;
+ auto last = prev(v.end());
+ copy(v.begin(), last, ostream_iterator<typename C::value_type>(oss, delim.c_str()));
+ oss << *last;
+ return oss.str();
+}
+
+inline vector<string> explode(const string &s, string delim=" ") {
+ vector<string> out;
+ size_t start = 0, end = 0;
+ while ((end = s.find(delim, start)) != string::npos) {
+ out.push_back(s.substr(start, end - start));
+ start = end + 1;
+ }
+ out.push_back(s.substr(start));
+ return out;
+}
+
#include "lang/verify.h"
#include "threaded_log.h"
+#define MEMBERS(...) \
+inline auto _tuple_() -> decltype(tie(__VA_ARGS__)) { return tie(__VA_ARGS__); } \
+inline auto _tuple_() const -> decltype(tie(__VA_ARGS__)) { return tie(__VA_ARGS__); }
+
+#define LEXICOGRAPHIC_OPERATOR(_c_, _op_) \
+inline bool operator _op_(const _c_ &b) const { return _tuple_() _op_ b._tuple_(); }
+
+#define LEXICOGRAPHIC_COMPARISON(_c_) \
+LEXICOGRAPHIC_OPERATOR(_c_, <) LEXICOGRAPHIC_OPERATOR(_c_, <=) \
+LEXICOGRAPHIC_OPERATOR(_c_, >) LEXICOGRAPHIC_OPERATOR(_c_, >=) \
+LEXICOGRAPHIC_OPERATOR(_c_, ==) LEXICOGRAPHIC_OPERATOR(_c_, !=)
+
#endif