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:x5x␣␣␣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 sep and nosep).

  1. sepSet and sepVal/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 | " \"" | sepVal | "\"";
    1,␣$2,␣$3␣",␣$"
    sepSet( sout, " " );						// reset separator to " "
    sout | 1 | 2 | 3 | " \"" | sepGet( sout ) | "\"";
    123␣"␣"
    
    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;
    123
    
  2. sepSetTuple and sepTupleVal/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 | " \"" | sepTupleVal | "\"";
    1␣2␣3␣4␣5␣6␣"␣"
    sepSetTuple( sout, ", " );					// reset tuple separator to ", "
    sout | t1 | t2 | " \"" | sepGetTuple( sout ) | "\"";
    1,␣2,␣3␣4,␣5,␣6␣",␣"
    
  3. sepOff and sepOn globally toggle printing the separator.
    sout | sepOff | 1 | 2 | 3;					// turn off implicit separator
    123
    sout | sepOn | 1 | 2 | 3;					// turn on implicit separator
    1␣2␣3
    
  4. sep and nosep locally toggle printing the separator with respect to the next printed item, and then return to the global separator setting.
    sout | 1 | nosep | 2 | 3;					// turn off implicit separator for the next item
    12␣3
    sout | sepOff | 1 | sep | 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 | nosep | t2;						// turn off implicit separator for the next item
    1,␣2,␣34,␣5,␣6
    
    sep 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 | sep | 1 | 2 | 3 | sep;				// sep does nothing at start/end of line
    1␣2␣3
    sout | sepVal | 1 | 2 | 3 | sepVal ;		// use sepVal 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 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" ) | wd( 10, "" ) | 'X';
    ␣␣abcd␣abcdefgh␣␣␣␣abc␣␣␣␣␣␣␣␣␣␣X
    
    Note, printing the null string with minimum width L pads with L spaces.
  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 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∀ streams contain a recursive lock and the stream has the lock trait so it can be used with the mutex statement.

The common usage is the short form of the mutex statement for expression statements, e.g.:

thread1 : mutex( sout ) sout | "abc " | "def ";
thread2 : mutex( sout ) sout | "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, the long form of the mutex statement is used, e.g.:

mutex( sout ) {
	sout | 1;
	mutex( sout ) sout | 2 | 3;				// unnecessary, but ok because of recursive lock
	sout | 4;
} // implicitly release sout lock

Note, the unnecessary mutex in the middle of the mutex statement, 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:

mutex( sin ) {
	int x, y, z, w;
	sin | x;
	mutex( sin ) sin | y | z;				// unnecessary, but ok because of recursive lock
	sin | w;
} // implicitly release sin lock

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.:

mutex( sout ) sout | "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 );
mutex( sout ) sout | acquire | "data:" | data;

Locale

Cultures use different syntax, called a locale, for printing numbers so they are easier to read, e.g.:

12,345.123		// comma separator, period decimal-point
12.345,123		// period separator, comma decimal-point
12345,123.		// space separator, comma decimal-point, period terminator

A locale is selected with function setlocale, and the corresponding locale package must be installed on the underlying system; setlocale returns 0p if the requested locale is unavailable. Furthermore, a locale covers the syntax for many cultural items, e.g., address, measurement, money, etc. This discussion applies to item LC_NUMERIC for formatting non-monetary integral and floating-point values. The following example shows selecting different cultural syntax, which may be associated with one or more countries.

#include <fstream.hfa>
#include <locale.h>							// setlocale
#include <stdlib.h>							// getenv

int main() {
	void print() {
		sout | 12 | 123 | 1234 | 12345 | 123456 | 1234567;
		sout | 12. | 123.1 | 1234.12 | 12345.123 | 123456.1234 | 1234567.12345;
		sout | nl;
	}
	sout | "Default locale off";
	print();
	sout | "Locale on" | setlocale( LC_NUMERIC, getenv( "LANG" ) );  // enable local locale
	print();
	sout | "German" | setlocale( LC_NUMERIC, "de_DE.UTF-8" );  // enable German locale
	print();
	sout | "Ukraine" | setlocale( LC_NUMERIC, "uk_UA.utf8" );  // enable Ukraine locale
	print();
	sout | "Default locale off" | setlocale( LC_NUMERIC, "C" );  // disable locale
	print();
}

Default locale off
12 123 1234 12345 123456 1234567
12. 123.1 1234.12 12345.123 123456.1234 1234567.12345

Locale on en_US.UTF-8
12 123 1,234 12,345 123,456 1,234,567
12. 123.1 1,234.12 12,345.123 123,456.1234 1,234,567.12345

German de_DE.UTF-8
12 123 1.234 12.345 123.456 1.234.567
12. 123,1. 1.234,12 12.345,123 123.456,1234 1.234.567,12345

Ukraine uk_UA.utf8
12 123 1 234 12 345 123 456 1 234 567
12. 123,1. 1234,12. 12345,123. 123456,1234. 1234567,12345.

Default locale off C
12 123 1234 12345 123456 1234567
12. 123.1 1234.12 12345.123 123456.1234 1234567.12345