#include <iostream>
#include <string>
using namespace std;
#include <sto/sto.h>
using namespace sto;
int main() {
string s = "Hello World!";
cout << (s * toupper) << endl;
}
STO is C++ headers only library, which provides alternative interface to STL and map/reduce/filter/range-expressions capabilities.
Requires: C++11 compiler. Tested with GCC-4.7.2, GCC-4.8(pre release), CLANG-3.2(pre release)
License: Boost Software License
STO was originally part of SCC ( C++ snippet evaluator at shell prompt). It does not depends on SCC but we will use it in examples, so we need to tell about SCC first.
Without SCC, minimal complete example for operator*(Range,Functor) would be:
#include <iostream>
#include <string>
using namespace std;
#include <sto/sto.h>
using namespace sto;
int main() {
string s = "Hello World!";
cout << (s * toupper) << endl;
}
With SCC, equivalent which also shows output, is much shorter:
scc "str s = "Hello World!"; s * toupper' HELLO WORLD!
1st line in above is what you type at your shell prompt. 2nd line is output. SCC includes all standard libraries includes; and it sends last statement-expression, if not terminated by semi-colon, to std::cout. Typedef str is shortcut for std::string, similarly vint (in examples that will follow) is std::vector<int>.
STO print operator can print almost any STL objects (containers, tuples, etc) directly. Items in print operator (and last expression which will be printed) are separated by , not by <<.
scc 'auto r = range(0,1,.2); r, endl, r * sin'
{0, 0.2, 0.4, 0.6, 0.8, 1}
{0, 0.198669, 0.389418, 0.564642, 0.717356, 0.841471}
Print operator are _ and __. Longer one terminates output with endl. With these, previous example would be:
scc '__ range(0,1,.2); __ range(0,1,.2) * sin;'
This is all you need to know about SCC. You can read more, if you wish on SCC page. To be able to run examples you need to install STO and SCC
Ranges are generalization of STL containers. They are STO ranges, STL containers, C-arrays and C-strings. STO defines C-strings as zero terminated arrays of char (not signed char or unsigned chars).
STO defines operators to work with ranges (and STL containers). It have two groups of operators. First is shortcuts for containers' member functions and simple extensions. Second is more complex operations like fold, reduce, filter and arbitrary transformation of ranges.
In table below are 1st group - simple shortcuts for container member functions. Unlike STL member functions (which often return void), they usually return result of operation (so it can be used in expressions).
+rg // rg.begin() -rg // rg.end() ++rg // rg.front() rg++ // rg.back() --rg // rg.pop_front() rg-- // rg.pop_back() rg << value // rg.push_back(value) value >> rg // rg.push_front(value) rg >> value // value = rg.back(value); rg.pop_back(); value << rg // value = rg.front(value); rg.pop_front(); rg1 << rg2 // copy(rg2.begin(), rg2.end(), back_inserter(rg1)); rg1 >> rg2 // copy(rg1.rbegin(), rg1.rend(), front_inserter(rg2)); ++pair // pair.first pair++ // pair.second ++tuple // get<0>(tuple) tuple++ // get<tuple_size<tuple<Types...> >::value-1>(tuple) stack << value // stack.push(value) stack++ // stack.top() stack-- // stack.pop() stack >> value // value = stack.top(value); stack.pop() queue++ // queue.back() ++queue // queue.front() --queue // queue.pop() queue << value // queue.push(value) value << queue // value = queue.front(value); queue.pop() rg = rg2 // underlying containers can be of different type rg = value // fill(rg.begin(), rg.end(), value) // SEARCH rg / value // find(+rg, -rg, value) rg % value // find(+rg, -rg, value) != -rg rg / Pred // find_if(+rg, -rg, pred) rg % Pred // find_if(+rg, -rg, pred) != -rg rg / rg // search(+rg, -rg, +rg, -rg) // GENERIC ERASE rg - value // erase all elements equal to value rg - pred // erase all elements for which pred evaluated to 'true` rg - rg2 // erase subrange rg - it // erase at iterator
STO Ranges are ranges that have extended STL compliant interface. They also do not contain range elemets data, they refer to an existing STL container or generate its elements.
STO Ranges are: iterator ranges, lazy-expression ranges and numeric ranges. Lazy means that its elements are calculated at the moment when particular element is accessed. Exact type of the range often is not important - these are types mostly of temporaries.
Numeric range is simple numeric sequence generator :
// [0,N) open-ended range. Only range from 1-arg range() is open-ended.
scc 'range(5)'
{0, 1, 2, 3, 4}
// [0,N] closed range
scc 'range(1,5)'
{1, 2, 3, 4, 5}
// floating point
scc 'range(1,5,0.5)'
{1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5}
// negative step
scc 'range(10,0,-1.5)'
{10, 8.5, 7, 5.5, 4, 2.5, 1}
// any arithmetic type
scc 'range('a','z')'
a b c d e f g h i j k l m n o p q r s t u v w x y z
// no need for verbose iota
scc 'vint V = range(5); V'
{0, 1, 2, 3, 4}
// is lazy
scc 'auto NR = range(1,999999999999999999l); *find(NR.begin(), NR.end(), 5)'
5
Iterator range is simple wrapper for pair of iterators.
Assign 42 to elements 2..4 of vector {1,2,3,4,5,6,7,8,9,10}:
scc 'vint V=range(1,10); range(V/2, V/5) = 42; V'
{1, 42, 42, 42, 5, 6, 7, 8, 9, 10}
Expression range(1,10) constructs numeric range which initialize V. Expression V/2 returns iterator to 1st element with value 2. Expression range(V/2,V/5) constructs iterator range from pair of iterators.
Ranges can be assigned to another range even if they have different underlying containers. Here is simple lazy-expression range, which shows that it just a reference to underlying container:
scc 'vint V; auto R=range(V); R=range(1,10); R, V'
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
We first assign a c-string, then append "XYZ" and then remove "bc" substring :
scc 'char s[99]; range(s) = "abc"; (range(s) << "XYZ") - "bc"' aXYZ
Note that we can not define overloaded operations if arguments are only POD types (like c-string). C++ do not allow this. This is a reason why we have to wrap s with range(). For binary ops, we need to wrap at least one of operands in a class.
Same example, but for str::string (without range-wrapping):
scc 'str s("abc"); (s << "XYZ") - "bc"'
aXYZ
Operator sto::operator* (Range,Functor) is map/transform operator, where Functor have signature compatible with return_type(*)(range_elem_type). Examples where return_type is the same as elem_type:
scc 'range("abc") * toupper'
ABC
scc 'vint V{-1,0,1}; V * abs'
{1, 0, 1}
In above, toupper is defined in functor.h, abs is taken from std::.
Expression V * abs is equivalent to:
std::transform(V.begin(), V.end(), V.begin(), std::abs<int>)'
Example with arbitrary return_type:
scc 'vector<const char*> V{"aa", "bbb"}; V * strlen'
{2, 3}
Length of longest string:
scc 'vector<str> V{"aa", "bbb", "c"}; V * size || max'
3
Functor size_t sto::size(Range) is similar to strlen but will work with any Range.
Operator sto::operator|| (Range,Functor) is fold operator.
scc 'vint V{1,2,3}; V || plus<int>()'
6
scc 'vint V{1,2,3}; V || add' // add - STO shortcut for std::plus<T>(), T - deduced
6
scc 'vint V{1,2,3}; V || mul' // mul - STO shortcut for std::multiplies<T>()
6
scc 'vstr V{"aa", "bb", "cc"}; V || add'
aabbcc
scc 'vint V{1,2,3}; V || min' // uses std::min
1
scc 'vstr V{"aaa", "bb", "cccc"}; V * size || min'
2
Expression V || min is equivalent to:
std::accumulate (
std::next(V.begin()),
V.end(),
V.front(),
static_cast<const int& (*)(const int&, const int&)>(std::min<int>)
);
Find (brute force algorithm) maximum of cos(x) for 8 < x < 9:
scc 'range(8, 9, 0.01) * cos || max' -0.1455
Pipe operator have much more freedom on what it can do with range. It can filter out, resize, reorder and convert to different type. Perfomed operation depends on type of right-hand-side object.
Filter are defined with operators|(Range, Predicate) and operator|(Range, elem_type).
Predicate is functor with signature bool(*)(elem_type). If such is used with pipe operator, all elements which evaluate to false with predicate - are filtered out:
scc 'range("abc-123") | isdigit'
123
Filter and transform:
scc '(range("abc-123, xyz/") | isalnum) * toupper'
ABC123XYZ
Hide digits:
scc "str S=\"John Q Public (650)1234567\"; S|isdigit='X'; S" John Q Public (XXX)XXXXXXX
Expression S | isdigit, produce a filtered range, where isdigit is predicate for filtering. This is lazy-evaluated, it does not creates temporary container and should be as efficient as hand written for-loop.
Filter operator|(Range, range_elem_type) selects only elements which are equal to second argument.
Count number of 2 elements, then replace 2 with 42:
scc 'vint V{1,2,3,2}; size(V|2)'
2
scc 'vint V{1,2,3,2}; V|2 = 42; V'
{1, 42, 3, 42}
Something interesting:
scc 'vint V{1,2,3,2}; (V|2 = 42)'
{}
Why empty range? Because this is range with all elements in V equal to 2. But all 2 were replaced. Underling container V is now {1, 42, 3, 42}, and {1, 42, 3, 42} | 2 is empty range.
Simple operator|(Range, void(*)(It,It)) -> Range matches STL algorithm which have 2 argument iterators:
scc 'vint{3,1,2} | sort | reverse'
{3, 2, 1}
Unlike other operators, it does not return new lazy-expression-range object, but the same range that was on input, modified in place by corresponding stl-algorithm. Above example is equivalent to:
scc 'vint V{3,1,2}; sort(+V,-V); reverse(+V,-V); V'
{3, 2, 1}
Another signature which can be matched is operator|(Range, It(*)(It,It)) - this can match algorithms like std::unique:
scc 'vint{3,1,2,3} | sort | unique'
{1, 2, 3}
Which is equivalent to:
scc 'vint V{3,1,2,3}; sort(+V,-V); auto e=unique(+V, -V); V.erase(e,-V); V'
{1, 2, 3}
Functors are function object, lambdas and plain functions.
Regrettably not all function, are usable with STO. For example STO relies on fact that functor have argument of the same type as range element type. But for examples ctype.h functions, which work with c-strings, have not char but int as parameter and they can be defined as macros. And what makes it even worse, they are randomly injected into default namespace by LIBSTDC++ . As workaround STO put equivalent functions with correct signatures into sto namespace.
Also compiler have its own picularities. Following compiles with CLANG, but not with GCC:
scc 'map<int,int> M{{1,11}, {2,22}}; M * get<0>'
{1, 2}
STO also defines some generic helper functions:
size(range) -> size_t
empty(range) -> bool
endz(range) -> It — like std::end(StlContainer), but will work with C-strings too
clear(range) -> void
all ctype functions reimplemented
is_odd, is_even
add,sub, mul and div - function object equivalent to std::plus<T>(), etc
Instead of std::cout printing, in SCC you can use so called bar-print. Below are bar-print statements with equivalent code in comments:
_ x; // cout << x; __ x; // cout << x << endl; __ x, y; // cout << x << " " << y << endl;
On last line " " will not be printed if x or y are strings.
On exit, SCC also checks if last output was unterminated with linefead. If true, it will add a linefead. Let say if last statement was cout << x;, then SCC will add cout << endl; on exit.
Standard library includes std::ostream_iterator. Unlike std::cout it accepts only one specific type (specified at construction time) and it is not pre-defined object. So to use it with STL algorithms, you need call its quite verbose constructor.
Let say we have vector<int> V; and set<string> S; and we want to print these. With std::ostream_iterator:
copy(V.begin(), V.end(), ostream_iterator<int> (cout, " ")); copy(S.begin(), S.end(), ostream_iterator<string>(cout, " "));
Include io.h defines oi object - SCC replacement for ostream_iterator, which can work with any type and is pre-defined. With oi, above example will be:
copy(V.begin(), V.end(), oi); copy(S.begin(), S.end(), oi);
Same using range operators, more about these later:
copy(+V, -V, oi); copy(+S, -S, oi);
Also assigning a container to oi is equivalent to doing std::copy, so still shorter:
oi = V; oi = S;
Any STL container can be also printed without oi. You can just send it to std::cout or bar-print it.
In below example __ C; can be replaced with cout << C;. Equivalent code and output is shown in comments:
vint C {1,2,3}; __ C; // vector<int> C{1,2,3};
// cout << "{";
// for(auto it=C.begin(); it!=C.end()-1; it++)
// cout << *it << ", ";
// if(!C.empty()) cout << C.back();
// cout << "}\n";
// prints: {1, 2, 3}
int C[] {1,2,3}; __ C; // {1,2,3}
array<int,3> C {1,2,3}; __ C; // {1,2,3}
tuple<int,int> C {1,2}; __ C; // ⟨1, 2⟩
map<int,int> C {{1,2},{3,4}}; __ C; // {⟨1,2⟩, ⟨3,4⟩}
vector<vint> C {{1,2},{3,4}}; __ C; // {{1, 2}, {3, 4}}
Same for input:
echo 1 2 | scc 'vint C; cin >> C; C' // {1,2}
echo 1 2 | scc 'int C[2]; cin >> C; C' // {1,2}
echo 1 2 | scc 'tuple<int,int> C; cin >> C; C' // ⟨1,2⟩
echo 1 2 | scc 'array<int,2> C; cin >> C; C' // {1,2}
echo 1 2 | scc 'set<int> C; cin >> C; C' // {1,2}
echo 1 2 3 4| scc 'map<int,int> C; cin >> C; C' // {⟨1,2⟩, ⟨3,4⟩}
If a container have non-zero length, then the corresponding number of elements will be read. If container is empty then input will be EOF-terminated.
Pre-defined object in can be used as value in expression which is read from std::cin.
int i(in); // int i; cin >> i; float x = 1.1 + float(in); // float y; cin >> y; float x = 1.1 + y;
|
|
Object in does not check for EOF. Use only in context where you can ignore or do not expect EOF. |
To input a container:
vint V = in(10); vint W = in; // Till EOF. Note: we can not use: vint W(in);
Here is example of C++ vs SCC input where we need to read a number and than corresponding number of container elements:
int N;
cin >> N;
vector<int> V(N);
for(int i=0; i<N; i++)
cin >> V[i];
With object in:
vint V = in(in);
File io.h can be used as standalone, independent include.
#include <io.h>
int main() {
__ "hello world";
}
Includes simple.h, cj.h and stl.h can also be used independently.
git clone http://github.com/lvv/sto /PATH/TO/INSTALL/STO/
echo '
#include <sto/sto.h>
using namespace sto;
int main() {
return range(10) || add;
}
' > /tmp/sto-test.cc
# test compile
CXXFLAGS+='-I/PATH/TO/INSTALL/ -std=c++11' make /tmp/sto-test && /tmp/sto-test
If you install in includes default-searchable directory (like /usr/include or /usr/local/include), you do not need to specify -I /path/to/install/ compiler option.
For CLANG use following options:
CXX=clang++ CXXFLAGS+="-D__STRICT_ANSI__ -std=c++0x "
To be able to run examples you need to install SCC too.
STO was tested with GCC-4.7.2/4.8-pre-release and CLANG-3.2-pre-release.