STO is C++ headers only library, which provides alternative interface to STL and map/reduce/filter/range-expressions capabilities.

SCC

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 and Range Operators

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

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

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

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}

Operation with C-strings

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

Operators: Map, Reduce, Filter

Map/Transform

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.

Reduce/Fold

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

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: range|predicate

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.

Evaluate: range|stl-algorithm

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}

Available Functors

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

Simple IO

Bar-print

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.

OI - Generic ostream_iterator

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;

STL Containers input / output

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.

Object IN

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;
Warning 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);

IO.H - Standalone Use

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.

Install

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.

Status

STO was tested with GCC-4.7.2/4.8-pre-release and CLANG-3.2-pre-release.