www.digitalmars.com         C & C++   DMDScript  

D - printf and read/write a proposal

reply Patrick Down <Patrick_member pathlink.com> writes:
In the spirt of the current printf and streamed I/O
debate I'd like to offer this idea.

I know that there is resistance to adding keywords to
the language however reading and writing are such a common 
operations that perhaps they is worthy of their own special
language support.  

I would propose the addition of two keywords to the language
'read' and 'write'.

Syntax: 

readstatement -> 'read' dataSourceObj ',' ioElement [ ',' ioElement ]+ ';'
writestatement -> 'write' dataSinkObj ',' ioElement [ ',' ioElement ]+ ';'

ioElement -> constant | variable | '.' functionName '(' paramList ')'


Some examples using these new keywords: 

// Example 1
write stdOut, "Hello there"; 

char[] strFoo = "foo";
char[] strBar = "foo";
int foo = 12;
float bar = 1.2;

// Example 2
write stdOut,"The value of ",str," is ",strFoo,"."; 

// Example 3
write stdOut("The value of {0} is {1}"), strFoo, foo; 

// Example 4
write stdOut("The value of {0} is {1}"), strBar, .format(bar,4,2); 

// Example 5
read stdIn, strFoo;

The compiler translates the 'read' or 'write' statement into a sequence of 
member function calls the data source or sink object specified in the statement.

In example 1 the compiler would translate the statement into:

stdOut.opWrite("Hello there");
stdOut.opWriteEnd();

Example 2 would be:

stdOut.opWrite("The value of ");
stdOut.opWrite(str);
stdOut.opWrite(" is ");
stdOut.opWrite(strFoo);
stdOut.opWrite(".");
stdOut.opWriteEnd();

The compiler tries to match up the types of the constants and variables in the
list that follows to opWrite or opRead member functions of the object;

An incomplete implementation of the console class for the examples above 
might be:

class StdOut
{

void opWrite(char[] value) { }
void opWrite(int value) { }
void opWrite(float value) { }

void opWriteEnd();

void format(float value, int digits, int postfix) { }

FormatedOutput opCall(char[] fmtStr) { return new FormatedOutput(this,fmtStr); }
}

class FormatedOutput
{
this(StdOut strOut, char[] fmtStr) { }

void opWrite(char[] value) { }
void opWrite(int value) { }
void opWrite(float value) { }

void opWriteEnd();

void format(float value, int digits, int postfix) { }
}

The opCall and FormatedOutput class allow what happens in examples 3 and 4.
Example 4 would be translated like this:

FormatedOutput temp = stdOut("The value of {0} is {1}");
temp.opWrite(strBar);
temp.format(bar,4,2);
temp.opWriteEnd();


The read objects are similar to the write objects except that they
implement opRead;


class StdIn
{
void opRead(out char[] value) {}
void opReadEnd() { }
}
Nov 07 2003
parent Charles Hixson <charleshixsn earthlink.net> writes:
Patrick Down wrote:
 In the spirt of the current printf and streamed I/O
 debate I'd like to offer this idea.
 
 I know that there is resistance to adding keywords to
 the language however reading and writing are such a common 
 operations that perhaps they is worthy of their own special
 language support.  
 
 I would propose the addition of two keywords to the language
 'read' and 'write'.
 
 Syntax: 
 
 readstatement -> 'read' dataSourceObj ',' ioElement [ ',' ioElement ]+ ';'
 writestatement -> 'write' dataSinkObj ',' ioElement [ ',' ioElement ]+ ';'
 
 ioElement -> constant | variable | '.' functionName '(' paramList ')'
 
 
 Some examples using these new keywords: 
 
 // Example 1
 write stdOut, "Hello there"; 
 
 char[] strFoo = "foo";
 char[] strBar = "foo";
 int foo = 12;
 float bar = 1.2;
 
 // Example 2
 write stdOut,"The value of ",str," is ",strFoo,"."; 
 
 // Example 3
 write stdOut("The value of {0} is {1}"), strFoo, foo; 
 
 // Example 4
 write stdOut("The value of {0} is {1}"), strBar, .format(bar,4,2); 
 
 // Example 5
 read stdIn, strFoo;
 
 The compiler translates the 'read' or 'write' statement into a sequence of 
 member function calls the data source or sink object specified in the
statement.
 
 In example 1 the compiler would translate the statement into:
 
 stdOut.opWrite("Hello there");
 stdOut.opWriteEnd();
 
 Example 2 would be:
 
 stdOut.opWrite("The value of ");
 stdOut.opWrite(str);
 stdOut.opWrite(" is ");
 stdOut.opWrite(strFoo);
 stdOut.opWrite(".");
 stdOut.opWriteEnd();
 
 The compiler tries to match up the types of the constants and variables in the
 list that follows to opWrite or opRead member functions of the object;
 
 An incomplete implementation of the console class for the examples above 
 might be:
 
 class StdOut
 {
 
 void opWrite(char[] value) { }
 void opWrite(int value) { }
 void opWrite(float value) { }
 
 void opWriteEnd();
 
 void format(float value, int digits, int postfix) { }
 
 FormatedOutput opCall(char[] fmtStr) { return new FormatedOutput(this,fmtStr);
}
 }
 
 class FormatedOutput
 {
 this(StdOut strOut, char[] fmtStr) { }
 
 void opWrite(char[] value) { }
 void opWrite(int value) { }
 void opWrite(float value) { }
 
 void opWriteEnd();
 
 void format(float value, int digits, int postfix) { }
 }
 
 The opCall and FormatedOutput class allow what happens in examples 3 and 4.
 Example 4 would be translated like this:
 
 FormatedOutput temp = stdOut("The value of {0} is {1}");
 temp.opWrite(strBar);
 temp.format(bar,4,2);
 temp.opWriteEnd();
 
 
 The read objects are similar to the write objects except that they
 implement opRead;
 
 
 class StdIn
 {
 void opRead(out char[] value) {}
 void opReadEnd() { }
 }
 
 

would propose that the initial list be: obj = Object.read (ioChannel | file) obj.write (ioChannel | file[, formatId]) obj.toXML() obj = Object.fromXML(xmlstring) Note: I'm not pretending that I got the syntax correct. I'm still in the early stages of learning D. The idea is that Object stands for a class-level method, and obj stands for some object instance. Here there would need to be some way for the read or fromXML method to determine the actual class of the object being read, and to create and return a new object of that kind. I'm not sure that this is feasible in D, but it would be very useful if it were. Each class would need to have a default constructor that could take the written object as input and create an instance of the class from that input. Of course, this would need to be overridden for some classes (and the write method would need to be overridden in symmetry). This would allow virtual members, etc. Note that each instance of formatId would need to be implemented separately. This might lead some classes to have a multitude of simple formatID methods, so perhaps some better solution is needed, but when one is talking about formatted output of class variables, then simple formatted solutions don't work well. Perhaps formatting could be simplified if one defined "views" of the class. But even this doesn't really solve the problem. Should the variable names be included? Are you doing a columnar list, with the variable names at the top? Do you want "optimum width" (for some definition of optimum) or do you want to specify? Are column separators needed? What are page breaks, and how do you deal with them? UGH! When Fortran was created, everything was monospaced, 66 lines / page (or change the carriage control tape the a custom punched one), and either 132 or 80 columns wide, depending on the paper choice. And only certain defined kinds of variable (float, integer, fixed length strings..the fancier types were later). Format was a good match to the problem space. Things are a bit more difficult when one starts dealing with user defined objects.
Nov 07 2003