- D Cookbook
- Adam D. Ruppe
- 884字
- 2021-07-16 11:50:48
Creating an output range
As input ranges serve as generators or iterators over data, output ranges serve as sinks for data. Output ranges may print data, perform one-way algorithms (such as message digests or hashes), collect data, or whatever else you can achieve with its required put
method.
Here, we'll write an output range that prints the arrays it receives out in hexadecimal format. We'll write sixteen bytes per line, always ending with a new line.
How to do it…
Let's create an output range by executing the following steps:
- Create a struct with a
put
method that takes the data we want to consume. As our hex dumper takes any kind of array and doesn't store it, we want invoid[]
. Theput
method will perform the printing we need. - Add any data we need to preserve between calls to
put
. To format our lines, we'll need to keep a count of how many items we've outputted so far. - Add other functions we need, such as destructors, constructors, or other methods. We want to ensure there's always a new line at the end, so we'll add a
writeln
function in the destructor. - Test the function with a static assert for
isOutputRange
for the types it must accept.
The code is as follows:
import std.range; import std.stdio; struct HexDumper { int outputted; void put(in void[] data) { foreach(b; cast(const ubyte[]) data) { writef("%02x ", b); outputted++; if(outputted == 16) { writeln(); outputted = 0; } } } ~this() { if(outputted) writeln(); } } static assert(isOutputRange!(HexDumper, ubyte)); static assert(isOutputRange!(HexDumper, char)); void main() { HexDumper output; output.put("Hello, world! In hex."); }
Run it and you'll see the following output:
48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 20 49 6e 20 68 65 78 2c
How it works…
An output range is much simpler than an input range. It only has one required method: put
. An output range has no required invariant; you are not required to set anything up in the constructor.
The put
method doesn't have to do anything in particular. Its only requirement is to accept a single argument of the type the range can handle. Typically, the put
method does one of the following three things:
- Consume the received data with an algorithm. The
std.digest
provides output ranges of this style. - Collect the data, for example, by building an array. The
std.array.Appender
is an output range of this style. - Display the received data. This is what our example does.
Using an output range at the core consists only of creating the object and calling the put
method to feed the object with data. Some ranges, such as the std.digest
ranges, also require calls to methods such as start
and finish
to inform the object that you have finished the process so that it can perform finalization. In our case, we used the destructor to finalize.
A destructor is declared in a struct with the syntax ~this() { /* code */ }
. The destructor runs immediately when the struct object goes out of scope.
Destructors are used for clean up—freeing memory, releasing resources, or finalizing an operation, with the limitation that the object is no longer available.
The std.digest
cannot use a destructor as its finish
method changes the data and returns the final result. A destructor cannot return a value; by the time the destructor runs, the object is no longer available, so it would be impossible to get the results. Our HexDumper
method can output a new line in the destructor, as that does not need to return data.
The implementation of our put
method loops over the data and writes it in hexadecimal using std.stdio.writef
. The loop includes a cast to const ubyte[]
because the function accepts generic data in the form of in void[]
. Any array will implicitly cast to void[]
, allowing us to pass anything to the put
method, including strings and other arrays. However, a void[]
array is only usable if it is explicitly casted to something else first. We want to print out the individual bytes, so we casted to const ubyte[]
. Then, the foreach
loop will go one byte at a time.
Finally, the writef
function works like writefln
, but does not print an automatic new line at the end. The writef
function is D's version of the classic C printf
function. The first argument is a format string. Our format string, "%02x"
, means pad with zeroes to at least two digits wide, and then put out the argument in hexadecimal, followed by a space.
There's more…
The std.range.put
function is a generic function that extends the ability of your output range to accept data. You only need to implement the core data type your function needs. Let other transformations such as accepting generic input ranges be handled by the standard library. An example of this can be seen in the Creating a digest recipe in Chapter 2, Phobos – The Standard Library.
See also
- http://dlang.org/phobos/std_format.html has the documentation on the
writef
function's format string
- 玩轉(zhuǎn)Scratch少兒趣味編程
- LabVIEW2018中文版 虛擬儀器程序設(shè)計(jì)自學(xué)手冊(cè)
- Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)
- Linux核心技術(shù)從小白到大牛
- 跟“龍哥”學(xué)C語言編程
- MongoDB for Java Developers
- Mastering Entity Framework
- JavaScript動(dòng)態(tài)網(wǎng)頁開發(fā)詳解
- Mastering Predictive Analytics with Python
- C++面向?qū)ο蟪绦蛟O(shè)計(jì)習(xí)題解答與上機(jī)指導(dǎo)(第三版)
- Keras深度學(xué)習(xí)實(shí)戰(zhàn)
- Python High Performance Programming
- JavaScript程序設(shè)計(jì)(第2版)
- Illustrator CC平面設(shè)計(jì)實(shí)戰(zhàn)從入門到精通(視頻自學(xué)全彩版)
- 寫給大家看的Midjourney設(shè)計(jì)書