File: csvtable.yo

package info (click to toggle)
bobcat 6.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 13,960 kB
  • sloc: cpp: 18,954; fortran: 5,617; makefile: 2,787; sh: 659; perl: 401; ansic: 26
file content (638 lines) | stat: -rw-r--r-- 27,411 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
includefile(include/header)

COMMENT(manpage, section, releasedate, archive, short name)
manpage(FBB::CSVTable)(3bobcat)(_CurYrs_)(libbobcat-dev__CurVers_)
                    (Table Construction)

manpagename(FBB::CSVTable)(Sequentially fills tables row-wise)

manpagesynopsis()
    bf(#include <bobcat/csvtable>)nl()
    Linking option: tt(-lbobcat)

manpagedescription()

bf(FBB::CSVTable) is used to fill tables row-wise. By default the table's
elements are comma-separated. The elements may contain any type of data that
can also be inserted into tt(std::ostreams), as may also contain horizontal
lines (optionally spanning multiple columns).

Before inserting elements into the table the widths, alignment types and
precisions of the table's columns are defined. By default values are
right-aligned.  While inserting table elements the alignment types and
precisions may be altered for specific elements, optionally spanning multiple
columns. When inserting values whose representations require more characters
than the current widths of the columns receiving those values then those
larger widths take precedence over the defined column widths.

Different from tables defined by bf(FBB::Table)(3bobcat) all data inserted
into tt(CSVTables) do not have to be completely available before the table
is inserted into a destination tt(std::ostream). As the table's column formats
are known before entering the data the tt(CSVTable) knows which format to use
for which column. These column format specifications may be defined in
multiple ways, e.g., by using text labels and values. tt(CSVTable) objects
always use the widest column specifications and alignment types that were
specified last.

When inserting elements into tt(CSVTables) the standard bf(C++) IO
manipulators can also be used. Table rows do not automatically end after the
table's last column has been filled. But when inserting elements beyond the
last column they are inserted as-is (but then the standard I/O format
specifications can still be used).

Table column definitions and table rows end at the end of insertion
statements (see below at the descriptions of the various tt(operator<<)
functions). E.g.,
        verb(
    CSVTable tab;
    ...
    tab << 1 << 2;              // two elements in this row
    tab << "one" << "two" << 3; // three elements in this row
        )

tt(CSVTable) uses two support classes handling, respectively, the definitions
of the characteristics of the table's columns and inserting values into the
table's elements. tt(CSVTabDef) handles the table's column definitions,
tt(CSVTabIns) handles insertions into the table elements. They offer various
insertion operators which are described below.

Constructing tables normally consists of two steps: first the characteristics
of the columns are defined, then values are inserted into the table's
elements. This sequence is not enforced by tt(CSVTable): after inserting
values into the table column definitions may be updated, whereafter
additional values may be inserted into the table which then use the updated
column definitions.

includefile(include/namespace)

manpagesection(INHERITS FROM)
    -

manpagesection(FMT)

bf(FBB::FMT) objects are returned by several free functions (like tt(left),
described below in section bf(FREE FUNCTIONS)), and tt(FMT) defines the
enumeration tt(Align) (see the next section) specifying alignment
types. tt(FMT) objects are internally used by tt(CSVTable) objects. A tt(FMT)
object specifies the width, the precision when floating point numbers are
inserted, the column's alignment type (left, right or centered), and the
number of table columns to use.

tt(FMT) objects can be inserted into tt(std::ostream) objects showing its
characteristics. tt(FMT) provides the following (const) accessors:
    itemization(
    itb(FMT::Align align())
        the alignment value;
    itb(unsigned nCols())
        the number of occupied columns;
    itb(unsigned precision())
       the precision used when inserting a floating point value (tt(~0U) (= -1
        as tt(int)) is returned if tt(precision) is not used). The insertion
        operator shows tt(precision: -1) when precision is tt(~0U);
    itb(unsigned width())
        the field width in number of characters;
    )

    The static member tt(char const *FMT::align(FMT::Align value)) returns the
textual label corresponding to tt(value).

manpagesection(ALIGN ENUM)

The tt(enum FMT::Align) defines values indicating the alignment types of the
table's columns:
    itemization(
    itb(FMT::Align::CENTER)
        The inserted information in the column is centered;
    itb(FMT::Align::LEFT)
        The inserted information is left-aligned;
    itb(FMT::Align::RIGHT)
        The inserted information is right-aligned (used by default);
    )

    In addition, when inserting horizontal lines, the value
    tt(FMT::Align::HLINE) is used.

manpagesection(CONSTRUCTORS)
    itemization(
    itb(CSVTable(std::ostream &out = std::cout,
                 std::string const &sep = ", "))
       This constructor by default writes its table to tt(std::cout) and uses
        a comma followed by a space character as column separator. During the
        table's construction the stream receiving the table can be altered
        using tt(stream) members, and the separator can be changed using the
        tt(sep) member, but the separator can also be changed while filling
        the table's elements (see below). When the tt(CSVTable) object goes
        out of scope the stream's original configuration is restored;

    itb(CSVTable(std::ofstream &&tmp, std::string const &sep = ", "))
       This constructor by default uses the same separator to separate the
        column's elements as the first constructor, but writes the table to
        tt(ofstream tmp), which is grabbed by tt(CSVTable);

    itb(CSVTable(std::string const &fname,
                 std::string const &sep = ", "),
                 std::ios::openmode mode = ios::out)
       This constructor by default uses the same separator to separate the
        column's elements as the first constructor, but writes the table to
        the file having (path)name tt(fname), by default (re)writing the
        file. If the file already exists and tt(CSVTable) should start writing
        at the file's end use, e.g., tt(ios::ate | ios::in). An exception is
        thrown if the specified file cannot be opened.
    )

The move constructor and move assignment operator are available; the copy
constructor and assignment operator are not available.

manpagesection(OVERLOADED OPERATORS)

In the provided examples tt(tab) refers to an existing tt(CSVTable)
object. Each insertion statement (note: not insertion em(expression)) either
defines or updates the table columns' definitions or fills the next row of the
table with data.

bf(Defining column characteristics)

The return types and left-hand side operands of the following insertion
operators are specified as tt(CSVTabDef). The member tt(fmt()) (cf. section
bf(MEMBER FUNCTIONS)) returns a tt(CSVTabDef) object which is then used in
combination with the following insertion operators to define the
characteristics of the table's columns.

    itemization(
    itb(CSVTabDef &operator<<(CSVTabDef &tab, FMT const &fmt))
       This insertion operator defines the characteristics of the next table
        column. tt(FMT) objects inserted into tt(CSVTabDef) objects must have
        been returned by tt(center, left) or tt(right) (see section bf(FREE
        FUNCTIONS), below), or an exception will be thrown. When redefining
        column specifications (e.g., when inserting tt(FMT) objects for
        previously defined columns) then the width of the wider column is
        used. Example:
    verb(
                // left align using 10 char. positions:
    tab.fmt() << FBB::left(10);
                // 1st col now right aligned, but its
                // width remains 10
    tab.fmr() << FBB::right(4);
    )

    itb(CSVTabDef &operator<<(CSVTabDef &tab, Type const &value))
       This operator is defined for the template type tt(Type) parameter
        tt(value), where tt(Type) values must be insertable in
        tt(std::ostreams). The (trimmed) width of tt(value) when inserted into
        an tt(ostream) defines the width of the next column, which is
        right-aligned. As width the previous insertion operator: if a previous
        definition specified a larger width, then that width is kept. Example:
       verb(
                // 2 columns, having widths 2 and 5:
    tab.fmt() << 12 << "hello";
       )
    )

bf(Inserting table elements)

In addition to the insertion operator actually inserting a value into the next
table's column(s) several format modifying insertion operators are
available. When a series of specifications are inserted before the actual
value is inserted then the specification inserted just before inserting the
table's value is used, overruling that column's default specification. Format
specifications other than those provided by the standard I/O manipulators are
ignored when used beyond the table's last column.

The return types and left-hand side operands of the following insertion
operators use tt(CSVTabIns) objects. tt(CSVTable's) conversion operator
tt(operator CSVTabIns()) described below returns a tt(CSVTabIns) object which
is used by the following insertion operators to insert values into the table.

    itemization(
    itb(CSVTabIns &operator<<(CSVTabIns &tab, FMT::hline))
       This operator inserts a horizontal line in the table's next column
        element. It is ignored when used beyond the table's last column;

    itb(CSVTabIns &operator<<(CSVTabIns &tab, (*FMT::hline)(unsigned
        nColumns)))
       This operator inserts a horizontal line spanning the next tt(nColumns)
        columns of the table. If the argument tt(nColumns) is omitted then a
        horizontal line is inserted spanning all of the table's remaining
        columns. When covering multiple columns no separators are used between
        the columns containing horizontal lines but one continuous horizontal
        line is used instead. The horizontal line is never written beyond the
        table's last column.

    itb(CSVTabIns &operator<<(CSVTabIns &tab, Type const &value))
       This operator is defined for the template type tt(Type) parameter
        tt(value), where tt(Type) values must be insertable in
        tt(std::ostreams). The value is inserted into the next table column,
        using the format specification that's active for that column. However,
        the specifications may be altered just before inserting the
        value. Values inserted beyond the table's last column are inserted
        as-is (although standard tt(I/O) manipulators can still be used);

    itb(CSVTabIns &operator<<(CSVTabIns &tab, FMT const &fmt))
       tt(FMT) objects are returned by several free functions defined in the
        tt(FBB) namespace (i.e., tt(center, left,) or tt(right), described
        below in section bf(FREE FUNCTIONS)). Example:
    verb(
            // left align using precision 2. E.g.,
            // e.g., '12.13     '
    tab << left(2) << 12.1278;
    )

    itb(CSVTabIns &operator<<(CSVTabIns &tab, FMT::Align align))
       The alignment argument can be tt(FMT::CENTER, FMT::LEFT) or
        tt(FMT::RIGHT). Example:
    verb(
            // centers '12' in its column,
            // e.g., '    12    '
    tab << FMT::CENTER << 12;
    )

    itb(void operator<<(CSVTabIns &tab,
                        std::ios_base &(*func)(std::ios_base &)))
       This insertion operator accepts manipulators like tt(std::left) and
        tt(std::right). When inserting these manipulators the next value to
        insert into the table is manipulated accordingly, overruling the next
        column's default specification.  Example:
       verb(
            // 'hi' is left-aligned, using the
            // using the default width and precision
    tab << std::left << "hi";
       )

    itb(CSVTabIns &operator<<(CSVTabIns &tab, Sep const &sep))
       The separator used when starting to insert values into the table's next
        row is changed to the separator specified by tt(sep). It remains
        active for the table's current row, also when inserting values beyond
        the table's last column. Example:
       verb(
            // writes, e.g., 'one, hi  there'
    tab << "one" << FMT::Sep{" "} << "hi" << "there";
       )

    itb(operator CSVTabIns())
       The conversion operator returns a tt(CSVTabIns) object which is used in
        combination with the above insertion operators to insert values into
        the next row of the table. Normally insertions start at column 1, but
        when called after calling tt(tab.more) (see below) then insertions
        continue after the last element that was inserted into
        tt(tab.more). Each time this conversion operator is used another row
        is added to the table. Insertions beyond the table's last column are
        processed, but tt(CSVTabIns's) insertion operators are ignored,
        inserting values as-is. However, in that case the standard
        tt(std::ostream) manipulators can also be used;

    itb(void operator()(std::string const &text))
       Calls tt(row(text, 0)) to insert the trimmed comma-separated elements
        of tt(text) into the table's next row;

    itb(FMT const &operator[](unsigned idx) const)
       Returns the default tt(FMT) specification of column tt(idx) (see also
        the description of the member tt(size()) below).
    )

manpagesection(MEMBER FUNCTIONS)

    In the provided examples tt(tab) refers to an existing tt(CSVTable)
    object.

    itemization(
    itb(std::vector<FMT> const &columns() const)
       Returns a reference to the vector containing the format specifications
        of the table managed by tt(CSVTable);

    itb(CSVTabDef &fmt(unsigned idx = 0))
       The elements inserted into the tt(CSVTabDef) object returned by
        tt(fmt()) define the specifications of the table's columns.
        Specifications start at column offset tt(idx), using 0 if not
        specified (its argument may not exceed the number of already defined
        columns or an exception is thrown). When called repeatedly for already
        specified columns then the widths of existing columns are kept if they
        exceed the widths of the corresponding inserted tt(FMT)
        elements. Repeated tt(fmt) calls may specify more columns than
        previous calls, in which case new columns are added to the table;

    itb(void fmt(std::string const &colSpecs, unsigned idx = 0))
       The comma-separated space-trimmed words of tt(colSpecs) define the
        widths of right-aligned table columns, starting at column index
        tt(idx), using 0 if not specified (its argument may not exceed the
        number of already defined columns or an exception is thrown). When
        called repeatedly for already specified columns then the widths of
        existing columns are kept if they exceed the lengths of the
        corresponding trimmed words. Repeated calls may specify more columns
        than previous calls, in which case additional columns are added to the
        table. Example:
       verb(
            // Define three right-aligned columns,
            // having widths of 3, 3 and 5.
    tab.fmt("one, two, three");
            // add columns 4 thru 6
    tab.fmt("one, two, three", 3);
       )

    itb(unsigned idx() const)
       The index of the column that will be used at the next insertion is
        returned. When inserting more values than the number of defined table
        columns then the return value of the member tt(size) is returned;

    itb(CSVTabIns more(unsigned idx = ~0U))
       When the default tt(idx) argument is used then values that are inserted
        into the returned tt(CSVTabIns) object are inserted beyond the
        last-used column of the table's current row (which may be the row's
        first element).

       When using another argument then insertions start in column tt(idx). If
        tt(dx) exceeds the last-used column index then intermediate columns
        remain empty.

       If tt(idx) is less than the column index that is used at the next
        insertion an exception is thrown.

       Insertions beyond the table's last column are processed, but then
        tt(CSVTabIns's) insertion operators are ignored, inserting values
        as-is. However, in that case the standard tt(std::ostream)
        manipulators can also be used;

       Following tt(more) the current row doesn't end, but values inserted
        next are inserted into the same row. Example:
       verb(
            // a row containing one element:
    tab << 1;
            // the next row contains 2 elements:
    tab.more() << 1 << 2;
            // now containing 4 elements
            // (element at idx 2 remains empty):
    tab.more(3) << 4;
            // completes the row, now having
            // 5 elements:
    tab << 5;
       )

       Following tt(more) calls the current row ends at the next tt(tab.row)
        call. If following tt(more) calls the current row should merely end
        then simply use tt(tab.row+nop()());

    itb(void more(std::string const &text, unsigned idx = ~0U))
       This member's tt(idx) parameter is handled as described at the previous
        member.

       The trimmed comma-separated elements of tt(text) are inserted into the
        current row, without ending the current row;

    itb(CSVTabIns row+nop()(unsigned idx = ~0U))
       This member's tt(idx) parameter and insertions into the returned
        tt(CSVTabIns) object are handled as described at the first tt(more)
        member, but the current row ends at the end of the statement. Example:
       verb(
            // a row containing one element:
    tab << 1;
            // the next row contains 2 elements:
    tab.more() << 1 << 2;
            // the now contains 4 elements
            // (element at idx 2 remains empty):
    tab.row(3) << 4;
       )

    itb(void row+nop()(std::string const &text, unsigned idx = ~0U))
       This member's tt(idx) parameter is handled as described at the first
        tt(more) member.

       The trimmed comma-separated elements of tt(text) are inserted into the
        current row, whereafter the row ends;

    itb(void  stream(std::ostream &out))
       After calling tt(tab.stream(out)) the table's construction continues
        at the next row using the stream tt(out);

    itb(void stream(std::ofstream &&tmp))
       After calling this member the table's construction continues at the
        next row using the tt(ofstream tmp), whih is grabbed by tt(CSVTable);

    itb(void stream(std::string const &fname,
                    std::ios::openmode mode = std::ios::out))
       After calling this member the table's construction continues at the
        next row using the (path)name tt(fname), by default (re)writing the
        file. If the file already exists and tt(CSVTable) should start writing
        at the file's end use, e.g., tt(ios::ate | ios::in). An exception is
        thrown if the specified file cannot be opened;

    itb(std::ostream &stream())
       A reference to the currently used stream is returned;

    itb(std::string const &sep() const)
       Returns the currently used default column separator;

    itb(void sep(std::string const &separator))
       Changes the currently used default column separator to
        tt(separator);

    itb(unsigned size() const)
       The number of defined columns is returned.
    )

manpagesection(FREE FUNCTIONS)

In the following examples tt(tab.fmt()) refers to a tt(CSVTabDef) object.

bf(Defining Column Characteristics)

The following functions are used to specify the alignment, width and optional
precision of columns. The first argument of these functions specifies the
column's width, the second argument is optional and specifies the column's
precision (used when inserting floating point values). The precision is only
used if its value is less than the column's width.

    itemization(
    itb(FMT center+nop()(unsigned width, unsigned precision = ~0U))
       When inserting this function's return value into tt(tab.fmt()) the
        values inserted into its column are centered in fields of tt(width)
        characters wide. Example:
       verb(
            // values are centered in fields of 10
            // characters wide, floating point values
            // use 3 digit behind the decimal point:
    tab.fmt() << center(10, 3);
       )

    itb(FMT center+nop()(std::string const &str, unsigned precision = ~0U))
       A convenience function calling tt(center(str.length(), precision));

    itb(FMT left(unsigned width, unsigned precision = ~0U))
       When inserting this function's return value into tt(tab.fmt()) the
        values inserted into its column are left-aligned in fields of
        tt(width) characters wide. Example:
       verb(
            // values are left-aligned in fields
            // of 5 characters wide.
    tab.fmt() << left(5);
       )
    itb(FMT left(std::string const &str, unsigned precision = ~0U))
       A convenience function calling tt(left(str.length(), precision));

    itb(FMT right(unsigned width, unsigned precision = ~0U))
       When inserting this function's return value into tt(tab.fmt()) the
        values inserted into its column are right-aligned in fields of
        tt(width) characters wide. Example:
       verb(
            // values are right-aligned in fields
            // of 5 characters wide.
    tab.fmt() << right(5);
       )
       Right-alignment is also used when using tt(CSVTab's fmt(std::string))
        member or when directly inserting values into tt(CSVTabDef) objects;

    itb(FMT right(std::string const &str, unsigned precision = ~0U))
       A convenience function calling tt(right(str.length(), precision)).
    )

bf(Inserting Table Elements)

In the following examples tt(tab) refers to a tt(CSVTable) object returning a
tt(CSVTabIns) object using its conversion operator.

Except for the function tt(hline) the following functions are used to alter
the column's default alignment and precision. The precision is only used if
its value is less than the column's width. By specifying tt(~0U) the precision
is ignored. If only the default alignment should be overruled then inserting
the corresponding tt(FMT::Align) value suffices.

Altering the default alignment of individual columns:

    itemization(
    itb(FMT center(precision))
       After inserting this function's return value into tt(tab) the value
        inserted next is centered, using tt(precision) when inserting floating
        point values.
       verb(
            // centers 9.87 in column 1
    tab << center(2) << 9.876";
       )

    itb(FMT left(precision))
       After inserting this function's return value into tt(tab) the value
        inserted next is left-aligned, using tt(precision) when inserting
        floating point values.
       verb(
            // left-aligns 9.87 in column 1
    tab << left(2) << 9.876";
       )


    itb(FMT right(precision))
       When inserting this function's return value into tt(tab) the value
        inserted next is right-aligned, using tt(precision) when inserting
        floating point values.
       verb(
            // right-aligns 9.87 in column 1
    tab << right(2) << 9.876";
       )
       By default tt(CSVTable) uses right-alignment.
    )

bf(Joining columns:)

Alignments specifications may span multiple columns. This is realized through
the tt(join) functions. When inserting a value after inserting the return
value of a tt(join) member then that value is inserted occupying all the
columns and using the alignment type specified when calling tt(join).  If
necessary the number of columns is reduced to avoid exceeding the table's last
column.

    itemization(
    itb(FMT join(unsigned nCols, FMT::Align align, unsigned precision = ~0U))
       A value that's inserted into the table after inserting tt(join's)
        return value occupies tt(nCols) columns, using alignment type
        tt(align), and optionally using tt(precision) when inserting floating
        point values. The alignment specification must be tt(FMT::CENTER,
        FMT::LEFT) or tt(FMT::RIGHT). Example:
    verb(
            // writes (assuming columns 2 and 3 occupy
            // 10 characters):
            //      left,    mid    , right
    tab << "left" << join(2, FMT::CENTER) << "mid" << "right"";
    )

    itb(FMT join(FMT::Align align, unsigned precision = ~0U))
       Same effect as the previous tt(join) function, but this function
        occupies all remaining columns of the table's current row (this can
        also be accomplished by calling the first tt(join) function specifying
        tt(~0U) as its first argument).
    )

bf(Inserting horizontal lines:)

If a single table element should contain a horizontal line then simply
inserting tt(Align::HLINE) works fine. The tt(hline) functions
are used to insert horizontal lines spanning one or more table columns.

    itemization(
    itb(FMT hline(unsigned nCols = ~0U))
       When inserting this function's return value into a tt(CSVTabIns) object
        a horizontal line spanning tt(nCols) columns is inserted into the
        table. If necessary tt(nCols) is reduced so that the horizontal line
        does not exceed the table's last column. When spanning multiple
        columns no column separated are used between the spanned columns: a
        single uninterrupted horizontal line is inserted. Example:
       verb(
            // columns 1 and 2: a horizontal line, column 3:
            // contains 'hi' (preceded by the column separator)
    tab << hline(2) << "hi";
        )
    )

manpagesection(EXAMPLE)
    verb(
#include <bobcat/csvtable>

using namespace FBB;

int main()
{
    CSVTable tab;

    tab.fmt() << "case" << right("length", 2) << right("weight", 1) <<
                           right("length", 2) << right("weight", 1);
    tab.sep("  ");

    tab << hline();
    tab << "" << join(4, FMT::CENTER) << "Gender";
    tab << "" << hline();

    tab << "" << join(2, FMT::CENTER) << "Female" <<
                 join(2, FMT::CENTER) << "Male";
    tab << "" << hline(2) << hline(2);

    tab << "Case" << "Length" << "Weight" << "Length" << "Weight";
    tab << hline();

    tab << 1 << 1.744 << 55.345 << 1.7244 << 64.801;
    tab << 2 << 1.58  << 57.545 << 1.8174 << 81.451;
    tab << 3 << 1.674 << 62.125 << 1.8244 << 80.201;

    tab << hline();
}
    )

    This program writes the following table to tt(std::cout):
    verb(
------------------------------------
                  Gender
      ------------------------------
          Female           Male
      --------------  --------------
Case  Length  Weight  Length  Weight
------------------------------------
   1    1.74    55.3    1.72    64.8
   2    1.58    57.5    1.82    81.5
   3    1.67    62.1    1.82    80.2
------------------------------------
    )

manpagefiles()
    em(bobcat/csvtable) - defines the class interface

manpageseealso()
    bf(bobcat)(7), bf(table)(3bobcat)

manpagebugs()
    None Reported.

includefile(include/trailer)