Stream Examples


Implicit Separator

By default, printed values are implicitly separated by whitespace. The implicit separator character (space/blank) is a separator not a terminator for output. The rules for implicitly adding the separator are:

  1. A separator does not appear at the start or end of a line.
    sout | 1 | 2 | 3;
    1 2 3
    
  2. A separator does not appear before or after a character literal or variable.
    sout | '1' | '2' | '3';
    123
    
  3. A separator does not appear before or after a null (empty) C string, which is a local mechanism to disable insertion of the separator character.
    sout | 1 | "" | 2 | "" | 3;
    123
    
  4. A separator does not appear before a C string starting with the (extended) ASCII characters: ,.;!?)]}%¢», where » is a closing citation mark.
    sout | 1 | ", x" | 2 | ". x" | 3 | "; x" | 4 | "! x" | 5 | "? x" | 6 | "% x"
    	 | 7 | "¢ x" | 8 | "» x" | 9 | ") x" | 10 | "] x" | 11 | "} x";
    1, x 2. x 3; x 4! x 5? x 6% x 7¢ x 8» x 9) x 10] x 11} x
    
  5. A separator does not appear after a C string ending with the (extended) ASCII characters: ([{=$£¥¡¿«, where ¡¿ are inverted opening exclamation and question marks, and « is an opening citation mark.
    sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x ¥"
    	 | 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10;
    x (1 x [2 x {3 x =4 x $5 x £6 x ¥7 x ¡8 x ¿9 x «10
    
  6. A separator does not appear before/after a C string starting/ending with the ASCII quote or whitespace characters: `'": \t\v\f\r\n
    sout | "x`" | 1 | "`x'" | 2 | "'x\"" | 3 | "\"x:" | 4 | ":x " | 5 | " x\t" | 6 | "\tx";
    x`1`x'2'x"3"x:4:x 5 x	6	x
    
  7. If a space is desired before or after one of the special string start/end characters, simply insert a space ().
    sout | "x (" | 1 | ") x" | 2 | ", x" | 3 | ":x:" | 4;
    x (1) x 2, x 3:x:4
    

Separation Manipulators

The following manipulators control implicit output separation. The effect of these manipulators is global for an output stream (except sepOn and sepOff).

  1. sepSet and sep/sepGet set and get the separator string. The separator string can be at most 16 characters including the '\0' string terminator (15 printable characters).
    sepSet( sout, ", $" );						// set separator from " " to ", \$"
    sout | 1 | 2 | 3 | " \"" | sep | "\"";
    1, $2, $3 ", $"
    sepSet( sout, " " );						// reset separator to " "
    sout | 1 | 2 | 3 | " \"" | sepGet( sout ) | "\"";
    1 2 3 " "
    
    sepGet can be used to store a separator and then restore it:
    char store[sepSize];						// sepSize is the maximum separator size
    strcpy( store, sepGet( sout ) );			// copy current separator
    sepSet( sout, "_" );						// change separator to underscore
    sout | 1 | 2 | 3;
    1_2_3
    sepSet( sout, store );						// change separator back to original
    sout | 1 | 2 | 3;
    1 2 3
    
  2. sepSetTuple and sepTuple/sepGetTuple get and set the tuple separator-string. The tuple separator-string can be at most 16 characters including the '\0' string terminator (15 printable characters). As for sepGet, sepGetTuple can be use to store a tuple separator and then restore it.
    [int, int, int] t1 = [1, 2, 3], t2 = [4, 5, 6];
    sepSetTuple( sout, " " );					// set tuple separator from ", " to " "
    sout | t1 | t2 | " \"" | sepTuple | "\"";
    1 2 3 4 5 6 " "
    sepSetTuple( sout, ", " );					// reset tuple separator to ", "
    sout | t1 | t2 | " \"" | sepGetTuple( sout ) | "\"";
    1, 2, 3 4, 5, 6 ", "
    
  3. sepDisable and sepEnable toggle printing the separator.
    sout | sepDisable | 1 | 2 | 3;				// turn off implicit separator
    123
    sout | sepEnable | 1 | 2 | 3;				// turn on implicit separator
    1 2 3
    
  4. sepOn and sepOff toggle printing the separator with respect to the next printed item, and then return to the global separator setting.
    sout | 1 | sepOff | 2 | 3;					// turn off implicit separator for the next item
    12 3
    sout | sepDisable | 1 | sepOn | 2 | 3;		// turn on implicit separator for the next item
    1 23
    
    The tuple separator also responses to being turned on and off.
    sout | t1 | sepOff | t2;					// turn off implicit separator for the next item
    1, 2, 34, 5, 6
    
    sepOn cannot be used to start/end a line with a separator because separators do not appear at the start/end of a line; use sep to accomplish this functionality.
    sout | sepOn | 1 | 2 | 3 | sepOn;			// sepOn does nothing at start/end of line
    1 2 3
    sout | sep | 1 | 2 | 3 | sep ;				// use sep to print separator at start/end of line
    ⊔︀1 2 3⊔︀
    

Newline Manipulators

The following manipulators control newline separation for input and output. For input:

  1. nl scans characters until the next newline character, i.e., ignore the remaining characters in the line.
  2. nlOn reads the newline character, when reading single characters.
  3. nlOff does not read the newline character, when reading single characters.

For example, in:

sin | i | nl | j;
1 2
3

variable i is assigned 1, the 2 is skipped, and variable j is assigned 3. For output:

  1. nl inserts a newline.
    sout | nl; 								// only print newline
    sout | 2; 								// implicit newline
    sout | 3 | nl | 4 | nl; 				// terminating nl merged with implicit newline
    sout | 5 | nl | nl; 					// again terminating nl merged with implicit newline
    sout | 6; 								// implicit newline
    
    2
    3
    4
    5
    
    6
    
    Note, a terminating nl is merged (overrides) with the implicit newline at the end of the sout expression, otherwise it is impossible to to print a single newline
  2. nlOn implicitly prints a newline at the end of each output expression.
  3. nlOff does not implicitly print a newline at the end of each output expression.

Output Value Manipulators

The following manipulators control formatting of output values (printing), and only affect the format of the argument.

  1. bin( integer ) print value in base 2 preceded by 0b/0B.
    sout | bin( 0 ) | bin( 27HH ) | bin( 27H ) | bin( 27 ) | bin( 27L );
    0b0 0b11011 0b11011 0b11011 0b11011
    sout | bin( -27HH ) | bin( -27H ) | bin( -27 ) | bin( -27L );
    0b11100101 0b1111111111100101 0b11111111111111111111111111100101 0b(58 1s)100101
    
  2. oct( integer ) print value in base 8 preceded by 0.
    sout | oct( 0 ) | oct( 27HH ) | oct( 27H ) | oct( 27 ) | oct( 27L );
    0 033 033 033 033
    sout | oct( -27HH ) | oct( -27H ) | oct( -27 ) | oct( -27L );
    0345 0177745 037777777745 01777777777777777777745
    
    Note, octal 0 is not preceded by 0 to prevent confusion.
  3. hex( integer / floating-point ) print value in base 16 preceded by 0x/0X.
    sout | hex( 0 ) | hex( 27HH ) | hex( 27H ) | hex( 27 ) | hex( 27L );
    0 0x1b 0x1b 0x1b 0x1b
    sout | hex( -27HH ) | hex( -27H ) | hex( -27 ) | hex( -27L );
    0xe5 0xffe5 0xffffffe5 0xffffffffffffffe5
    
    sout | hex( 0.0 ) | hex( 27.5F ) | hex( 27.5 ) | hex( 27.5L );
    0x0.p+0 0x1.b8p+4 0x1.b8p+4 0xd.cp+1
    sout | hex( -27.5F ) | hex( -27.5 ) | hex( -27.5L );
    -0x1.b8p+4 -0x1.b8p+4 -0xd.cp+1
    
  4. sci( floating-point ) print value in scientific notation with exponent. Default is 6 digits of precision.
    sout | sci( 0.0 ) | sci( 27.5 ) | sci( -27.5 );
    0.000000e+00 2.750000e+01 -2.750000e+01
    
  5. eng( floating-point ) print value in engineering notation with exponent, which means the exponent is adjusted to a multiple of 3.
    sout | eng( 0.0 ) | eng( 27000.5 ) | eng( -27.5e7 );
    0e0 27.0005e3 -275e6
    
  6. unit( engineering-notation ) print engineering exponent as a letter between the range 10-24 and 1024: y ⇒ 10-24, z ⇒ 10-21, a ⇒ 10-18, f ⇒ 10-15, p ⇒ 10-12, n ⇒ 10-9, u ⇒ 10-6, m ⇒ 10-3, K ⇒ 103, M ⇒ 106, G ⇒ 109, T ⇒ 1012, P ⇒ 1015, E ⇒ 1018, Z ⇒ 1021, Y ⇒ 1024. For exponent 100, no decimal point or letter is printed.
    sout | unit(eng( 0.0 )) | unit(eng( 27000.5 )) | unit(eng( -27.5e7 ));
    0 27.0005K -275M
    
  7. upcase( bin / hex / floating-point ) print letters in a value in upper case. Lower case is the default.
    sout | upcase( bin( 27 ) ) | upcase( hex( 27 ) ) | upcase( 27.5e-10 ) | upcase( hex( 27.5 ) );
    0B11011 0X1B 2.75E-09 0X1.B8P+4
    
  8. nobase( integer ) do not precede bin, oct, hex with 0b/0B, 0, or 0x/0X. Printing the base is the default.
    sout | nobase( bin( 27 ) ) | nobase( oct( 27 ) ) | nobase( hex( 27 ) );
    11011 33 1b
    
  9. nodp( floating-point ) do not print a decimal point if there are no fractional digits. Printing a decimal point is the default, if there are no fractional digits.
    sout | 0. | nodp( 0. ) | 27.0 | nodp( 27.0 ) | nodp( 27.5 );
    0.0 0 27.0 27 27.5
    
  10. sign( integer / floating-point ) prefix with plus or minus sign (+ or -). Only printing the minus sign is the default.
    sout | sign( 27 ) | sign( -27 ) | sign( 27. ) | sign( -27. ) | sign( 27.5 ) | sign( -27.5 );
    +27 -27 +27.0 -27.0 +27.5 -27.5
    
  11. wd( minimum, value ), wd( minimum, precision, value ) For all types, minimum is the number of printed characters. If the value is shorter than the minimum, it is padded on the right with spaces.
    sout | wd( 4, 34) | wd( 3, 34 ) | wd( 2, 34 );
    sout | wd( 10, 4.) | wd( 9, 4. ) | wd( 8, 4. );
    sout | wd( 4, "ab" ) | wd( 3, "ab" ) | wd( 2, "ab" );
      34  34 34
      4.000000  4.000000 4.000000
      ab  ab ab
    
    If the value is larger, it is printed without truncation, ignoring the minimum.
    sout | wd( 4, 34567 ) | wd( 3, 34567 ) | wd( 2, 34567 );
    sout | wd( 4, 3456. ) | wd( 3, 3456. ) | wd( 2, 3456. );
    sout | wd( 4, "abcde" ) | wd( 3, "abcde" ) | wd( 2,"abcde" );
    34567 34567 34567
    3456. 3456. 3456.
    abcde abcde abcde
    
    For integer types, precision is the minimum number of printed digits. If the value is shorter, it is padded on the left with leading zeros.
    sout | wd( 4,3, 34 ) | wd( 8,4, 34 ) | wd( 10,10, 34 );
     034     0034 0000000034
    
    If the value is larger, it is printed without truncation, ignoring the precision.
    sout | wd( 4,1, 3456 ) | wd( 8,2, 3456 ) | wd( 10,3, 3456 );
    3456     3456       3456
    
    If precision is 0, nothing is printed for zero. If precision is greater than the minimum, it becomes the minimum.
    sout | wd( 4,0, 0 ) | wd( 3,10, 34 );
         0000000034
    
    For floating-point types, precision is the minimum number of digits after the decimal point.
    sout | wd( 6,3, 27.5 ) | wd( 8,1, 27.5 ) | wd( 8,0, 27.5 ) | wd( 3,8, 27.5 );
    27.500     27.5      28. 27.50000000
    
    For the C-string type, precision is the maximum number of printed characters, so the string is truncated if it exceeds the maximum.
    sout | wd( 6,8, "abcd" ) | wd( 6,8, "abcdefghijk" ) | wd( 6,3, "abcd" );
      abcd abcdefgh    abc
    
  12. ws( minimum, significant, floating-point ) For floating-point types, minimum is the same as for manipulator wd, but significant is the maximum number of significant digits to be printed for both the integer and fractions (versus only the fraction for wd). If a value's significant digits is greater than significant, the last significant digit is rounded up.
    sout | ws(6,6, 234.567) | ws(6,5, 234.567) | ws(6,4, 234.567) | ws(6,3, 234.567);
    234.567 234.57  234.6    235
    
    If a value's magnitude is greater than significant, the value is printed in scientific notation with the specified number of significant digits.
    sout | ws(6,6, 234567.) | ws(6,5, 234567.) | ws(6,4, 234567.) | ws(6,3, 234567.);
    234567. 2.3457e+05 2.346e+05 2.35e+05
    
    If significant is greater than minimum, it defines the number of printed characters.
    sout | ws(3,6, 234567.) | ws(4,6, 234567.) | ws(5,6, 234567.) | ws(6,6, 234567.);
    234567. 234567. 234567. 234567.
    
  13. left( field-width ) left justify within the given field.
    sout | left(wd(4, 27)) | left(wd(10, 27.)) | left(wd(10, 27.5))
    	 | left(wd(4,3, 27)) | left(wd(10,3, 27.5));
    27   27.000000  27.500000  027  27.500    
    
  14. pad0( field-width ) left pad with zeroes (0).
    sout | pad0( wd( 4, 27 ) ) | pad0( wd( 4,3, 27 ) ) | pad0( wd( 8,3, 27.5 ) );
    0027  027 0027.500
    

Input Value Manipulators

The format of numeric input values in the same as C constants without a trailing type suffix, as the input value-type is denoted by the input variable. For bool type, the constants are true and false. For integral types, any number of digits, optionally preceded by a sign (+ or -), where a

For floating-point types, any number of decimal digits, optionally preceded by a sign (+ or -), optionally containing a decimal point, and optionally followed by an exponent, e or E, with signed (optional) decimal digits. Floating-point values can also be written in hexadecimal format preceded by 0x or 0X with hexadecimal digits and exponent denoted by p or P. For the C-string type, the input values are not the same as C-string constants, i.e., double quotes bracketing arbitrary text with escape sequences. Instead, the next sequence of non-whitespace characters are read, and the input sequence is terminated with delimiter '\0'. The string variable must be large enough to contain the input sequence.

The following manipulators control formatting of input values (reading), and only affect the format of the argument.

  1. skip( pattern ), skip( length ) The pattern is composed of white-space and non-white-space characters, where any white-space character matches 0 or more input white-space characters (hence, consecutive white-space characters in the pattern are combined), and each non-white-space character matches exactly with an input character. The length is composed of the next N characters, including the newline character. If the match successes, the input characters are discarded, and input continues with the next character. If the match fails, the input characters are left unread.
    char sk[] = "abc";
    sin | "abc " | skip( sk ) | skip( 5 );	// match input sequence
    abc⊔⊔⊔
    abc⊔⊔
    xx
    
  2. wdi( maximum, reference-value ) For all types except char, maximum is the maximum number of characters read for the current operation.
    char s[10];   int i;   double d;   
    sin | wdi( 4, s ) | wdi( 3, i ) | wdi( 8, d );  // c == "abcd", i == 123, d == 3.456E+2
    abcd1233.456E+2
    
    Note, input wdi cannot be overloaded with output wd because both have the same parameters but return different types. Currently, C∀ cannot distinguish between these two manipulators in the middle of an sout/sin expression based on return type.
  3. ignore( reference-value ) For all types, the data is read from the stream depending on the argument type but ignored, i.e., it is not stored in the argument.
    double d;
    sin | ignore( d );						// d is unchanged
      -75.35e-4 25
    
  4. incl( scanset, input-string ) For C-string types, the scanset matches any number of characters in the set. Matching characters are read into the C input-string and null terminated.
    char s[10];
    sin | incl( "abc", s );
    bcaxyz
    
  5. excl( scanset, input-string ) For C-string types, the scanset matches any number of characters not in the set. Non-matching characters are read into the C input-string and null terminated.
    char s[10];
    sin | excl( "abc", s );
    xyzbca
    

Concurrent Stream Access

When a stream is shared by multiple threads, input or output characters can be intermixed or cause failure. For example, if two threads execute the following:

thread1 : sout | "abc " | "def ";
thread2 : sout | "uvw " | "xyz ";

possible outputs are:

abc def
uvw xyz 
abc uvw xyz 
def 
uvw abc xyz def

abuvwc dexf
yz
uvw abc def 
xyz 

As a result, some form of mutual exclusion is required for concurrent stream access. C∀ provides a fine-grained solution where a recursive lock is acquired and released indirectly via a manipulator acquire or instantiating an RAII type specific for the kind of stream: osacquire for output streams and isacquire for input streams.

The common usage is manipulator acquire to lock a stream during a single cascaded I/O expression, with the manipulator appearing as the first item in a cascade list, e.g.:

thread1 : sout | acquire | "abc " | "def ";   // manipulator
thread2 : sout | acquire | "uvw " | "xyz ";

Now, the order of the thread execution is still non-deterministic, but the output is constrained to two possible lines in either order.

abc def
uvw xyz
uvw xyz
abc def

To lock a stream across multiple I/O operations, an object of type osacquire or isacquire is declared to implicitly acquire/release the stream lock providing mutual exclusion for the object's duration, e.g.:

{	// acquire sout for block duration
	osacquire acq = { sout };				// named stream locker
	sout | 1;
	sout | acquire | 2 | 3;					// unnecessary, but ok to acquire and release again
	sout | 4;
}	// implicitly release the lock when "acq" is deallocated
Note, the unnecessary acquire manipulator works because the recursive stream-lock can be acquired/released multiple times by the owner thread. Hence, calls to functions that also acquire a stream lock for their output do not result in deadlock. The previous values written by threads 1 and 2 can be read in concurrently:
{	// acquire sin lock for block duration
	isacquire acq = { sin };				// named stream locker
	int x, y, z, w;
	sin | x;
	sin | acquire | y | z;					// unnecessary, but ok to acquire and release again
	sin | w;
}	// implicitly release the lock when "acq" is deallocated

Again, the order of the reading threads is non-deterministic. Note, non-deterministic reading is rare.

WARNING: The general problem of nested locking can occur if routines are called in an I/O sequence that block, e.g.:

sout | acquire | "data:" | rtn( mon );		// mutex call on monitor

If the thread executing the I/O expression blocks in the monitor with the sout lock, other threads writing to sout also block until the thread holding the lock is unblocked and releases it. This scenario can lead to deadlock, if the thread that is going to unblock the thread waiting in the monitor first writes to sout (deadly embrace). To prevent nested locking, a simple precaution is to factor out the blocking call from the expression, e.g.:

int data = rtn( mon );
sout | acquire | "data:" | data;