FBB::CSVTable(3bobcat)

πŸ’– FBB::CSVTable πŸ’–

okay bestie, so this is FBB::CSVTable(3bobcat) β€” it lives under the "Table Construction" energy of the bobcat manual, and it is literally here to make your tables pop off row by row. no cap.


✨ NAME ✨

FBB::CSVTable β€” Sequentially fills tables row-wise. she's organized, she's sequential, she fills rows like it's her main character moment πŸ’…


✨ SYNOPSIS ✨

#include <bobcat/csvtable>

Linking option: -lbobcat


✨ DESCRIPTION ✨

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 std::ostreams, and they may also contain horizontal lines (optionally spanning multiple columns). yes bestie, horizontal lines too. iconic behavior.

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. the table is not gonna squish your data, periodt.

Different from tables defined by FBB::Table(3bobcat), all data inserted into CSVTables do not have to be completely available before the table is inserted into a destination std::ostream. As the table's column formats are known before entering the data, the 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. CSVTable objects always use the widest column specifications and alignment types that were specified last. so the last word wins, lowkey.

When inserting elements into CSVTables the standard 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 operator<< functions). E.g.,

CSVTable tab;
...
tab << 1 << 2;              // two elements in this row
tab << "one" << "two" << 3; // three elements in this row

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. CSVTabDef handles the table's column definitions, 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 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. so you can change your mind mid-build. we love flexibility 🫢


✨ NAMESPACE ✨

FBB

All constructors, members, operators and manipulators, mentioned in this man-page, are defined in the namespace FBB.


✨ INHERITS FROM ✨

-


✨ FMT ✨

FBB::FMT objects are returned by several free functions (like left, described below in section FREE FUNCTIONS), and FMT defines the enumeration Align (see the next section) specifying alignment types. FMT objects are internally used by CSVTable objects. A 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.

FMT objects can be inserted into std::ostream objects showing its characteristics. FMT provides the following (const) accessors:

  • FMT::Align align() β€” the alignment value;
  • unsigned nCols() β€” the number of occupied columns;
  • unsigned precision() β€” the precision used when inserting a floating point value (~0U (= -1 as int) is returned if precision is not used). The insertion operator shows precision: -1 when precision is ~0U;
  • unsigned width() β€” the field width in number of characters;

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


✨ ALIGN ENUM ✨

The enum FMT::Align defines values indicating the alignment types of the table's columns:

  • FMT::Align::CENTER β€” The inserted information in the column is centered;
  • FMT::Align::LEFT β€” The inserted information is left-aligned;
  • FMT::Align::RIGHT β€” The inserted information is right-aligned (used by default);

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


✨ CONSTRUCTORS ✨

  • CSVTable(std::ostream &out = std::cout, std::string const &sep = ", ") β€” This constructor by default writes its table to 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 stream members, and the separator can be changed using the sep member, but the separator can also be changed while filling the table's elements (see below). When the CSVTable object goes out of scope the stream's original configuration is restored;
  • 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 ofstream tmp, which is grabbed by CSVTable;
  • 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 fname, by default (re)writing the file. If the file already exists and CSVTable should start writing at the file's end use, e.g., 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.

✨ OVERLOADED OPERATORS ✨

Okay so, in all the examples down here, tab is a CSVTable object that already exists. And listen β€” every insertion statement (bestie, a statement, not an insertion expression, the page is being very specific about this 😭) either defines or updates the table columns' definitions, or it fills up the next row of the table with data. That's it. That's the whole job description. πŸ’…

🌸 Defining column characteristics 🌸

The return types and the left-hand side operands of these insertion operators are all specified as CSVTabDef. The member fmt() (peep the MEMBER FUNCTIONS section, it's iconic) hands you back a CSVTabDef object, and you use that object together with the operators below to define the characteristics of your table's columns. Very demure, very mindful. πŸŽ€

  • CSVTabDef &operator<<(CSVTabDef &tab, FMT const &fmt) β€” this one defines the characteristics of the next table column. FMT objects inserted into CSVTabDef objects must have been returned by center, left or right (see section FREE FUNCTIONS, below) β€” otherwise an exception gets thrown, no cap 🫠. When you're redefining column specifications (like, inserting FMT objects for columns you already defined), the width of the wider column wins. Example:
// 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);
  • CSVTabDef &operator<<(CSVTabDef &tab, Type const &value) β€” this operator is defined for the template type Type parameter value, where Type values must be insertable in std::ostreams. The (trimmed) width of value when it's inserted into an ostream is what defines the width of the next column, and that column is right-aligned. Same width rules as the previous insertion operator, fr fr: if a previous definition specified a larger width, then that width is kept. Example:
// 2 columns, having widths 2 and 5:
tab.fmt() << 12 << "hello";

πŸ“ Inserting table elements πŸ“

Besides the insertion operator that actually inserts a value into the next table's column(s), there are several format-modifying insertion operators available. And here's the tea β˜•: when you insert a whole series of specifications before the actual value goes in, the specification inserted just before you insert the table's value is the one that's used β€” it overrules that column's default specification. Format specifications that aren't the ones provided by the standard I/O manipulators get ignored when used beyond the table's last column. Noted. πŸ“

The return types and left-hand side operands of these insertion operators use CSVTabIns objects. CSVTable's conversion operator operator CSVTabIns() (described below) returns a CSVTabIns object, and that's what these insertion operators use to insert values into the table. 🫢

  • CSVTabIns &operator<<(CSVTabIns &tab, FMT::hline) β€” inserts a horizontal line in the table's next column element. It's ignored if you use it beyond the table's last column.
  • CSVTabIns &operator<<(CSVTabIns &tab, (*FMT::hline)(unsigned nColumns)) β€” inserts a horizontal line spanning the next nColumns columns of the table. If you leave the nColumns argument out, then the horizontal line spans all of the table's remaining columns. When it's covering multiple columns, no separators are used between the columns containing horizontal lines β€” instead it's one continuous horizontal line. And that line is never written beyond the table's last column. Boundaries, gorgeous. 🚧
  • CSVTabIns &operator<<(CSVTabIns &tab, Type const &value) β€” this operator is defined for the template type Type parameter value, where Type values must be insertable in std::ostreams. The value gets inserted into the next table column using the format specification that's active for that column. That said, the specifications can be altered right before you insert the value. Values inserted beyond the table's last column are inserted as-is (although standard I/O manipulators can still be used).
  • CSVTabIns &operator<<(CSVTabIns &tab, FMT const &fmt) β€” FMT objects are returned by several free functions defined in the FBB namespace (that's center, left, or right, all described below in section FREE FUNCTIONS). Example:
// left align using precision 2. E.g.,
// e.g., '12.13     '
tab << left(2) << 12.1278;
  • CSVTabIns &operator<<(CSVTabIns &tab, FMT::Align align) β€” the alignment argument can be FMT::CENTER, FMT::LEFT or FMT::RIGHT. Example:
// centers '12' in its column,
// e.g., '    12    '
tab << FMT::CENTER << 12;
  • void operator<<(CSVTabIns &tab, std::ios_base &(*func)(std::ios_base &)) β€” this insertion operator accepts manipulators like std::left and std::right. When you insert these manipulators, the next value you insert into the table is manipulated accordingly, overruling the next column's default specification. Example:
// 'hi' is left-aligned, using the
// using the default width and precision
tab << std::left << "hi";
  • CSVTabIns &operator<<(CSVTabIns &tab, Sep const &sep) β€” the separator used when you start inserting values into the table's next row gets changed to the separator specified by sep. It stays active for the table's current row, including when you're inserting values beyond the table's last column. Staying power πŸ’ͺ. Example:
// writes, e.g., 'one, hi  there'
tab << "one" << FMT::Sep{" "} << "hi" << "there";
  • operator CSVTabIns() β€” the conversion operator returns a 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 it's called after calling tab.more (see below) then insertions continue after the last element that was inserted into 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 CSVTabIns's insertion operators are ignored and the values are inserted as-is. In that case, though, the standard std::ostream manipulators can also be used. πŸ”₯
  • void operator()(std::string const &text) β€” Calls text, 0 to insert the trimmed comma-separated elements of text into the table's next row.
  • FMT const &operator[](unsigned idx) const β€” returns the default FMT specification of column idx (also check out the description of the member size() below, it's related).

🫢 MEMBER FUNCTIONS 🫢

okay bestie, this is the whole toolbox. everywhere down below, tab means an existing CSVTable object β€” like, the one you already made πŸ’…

  • std::vector<FMT> const &columns() const β€” hands you back a reference to the vector holding the format specifications of the table that CSVTable is managing. that's it, that's the whole vibe.
  • CSVTabDef &fmt(unsigned idx = 0) β€” whatever you insert into the CSVTabDef object this returns becomes the spec for the table's columns. the specifications start at column offset idx, or 0 if you didn't specify it (and fr fr, that argument may not exceed the number of already-defined columns, or an exception gets thrown at you 😭). if you call this again for columns you already specified, the widths of the existing columns are kept whenever they're bigger than the widths of the corresponding FMT elements you just inserted. and repeated fmt calls can totally specify more columns than previous calls did β€” those new columns just get added to the table. growth mindset ✨
  • void fmt(std::string const &colSpecs, unsigned idx = 0) β€” the comma-separated, space-trimmed words inside colSpecs set the widths of right-aligned table columns, starting at column index idx, or 0 if you didn't specify (again: that arg may not exceed the number of already-defined columns or it's exception city 🚨). call it repeatedly on columns you already set and the widths of the existing columns stick around as long as they're wider than the lengths of the corresponding trimmed words. repeated calls may also specify more columns than previous calls, and those extra columns get added to the table. example time:
        // 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);
  • unsigned idx() const β€” tells you the index of the column that's gonna be used at the next insertion. and if you're inserting more values than there are defined table columns, you get back whatever the member size returns. cute.
  • CSVTabIns more(unsigned idx = ~0U) β€” with the default idx argument, the values you insert into the returned CSVTabIns object land beyond the last-used column of the table's current row (which could be the row's very first element, no pressure). pass a different argument and insertions start at column idx. if that goes past the last-used column index, the columns in between just stay empty (ghost columns πŸ‘»). if idx is less than the column index used at the next insertion β†’ exception, thrown, over. insertions beyond the table's last column still get processed, but then CSVTabIns's insertion operators are ignored and the values are inserted as-is β€” however, in that case you can use the standard std::ostream manipulators too. also important: after more the current row does not end, the values you insert next go into the same row. example:
        // 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 more calls, the current row ends at the next tab.row call. if following more calls you just want the current row to end, simply use tab.row(); 🫑

  • void more(std::string const &text, unsigned idx = ~0U) β€” this member's idx parameter is handled exactly like the previous member's. the trimmed comma-separated elements of text get inserted into the current row, and the current row does not end. cozy.
  • CSVTabIns row(unsigned idx = ~0U) β€” this member's idx parameter and the insertions into the returned CSVTabIns object are handled like at the first more member, except the current row ends at the end of the statement. example:
        // 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;
  • void row(std::string const &text, unsigned idx = ~0U) β€” this member's idx parameter is handled like at the first more member. the trimmed comma-separated elements of text get inserted into the current row, and after that the row ends. bye bestie πŸ‘‹
  • void stream(std::ostream &out) β€” after calling tab.stream(out), the table's construction keeps going at the next row, using the stream out.
  • void stream(std::ofstream &&tmp) β€” after calling this member, the table's construction continues at the next row using the ofstream tmp, which is grabbed by CSVTable (it's hers now, she claimed it πŸ’œ).
  • 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 fname, and by default it (re)writes the file. if the file already exists and CSVTable should start writing at the file's end, use, e.g., ios::ate | ios::in. an exception is thrown if the file you named can't be opened 😀
  • std::ostream &stream() β€” returns a reference to the stream that's currently in use.
  • std::string const &sep() const β€” returns the currently used default column separator.
  • void sep(std::string const &separator) β€” changes the currently used default column separator to separator.
  • unsigned size() const β€” returns the number of defined columns.

βœ¨πŸ’– FREE FUNCTIONS πŸ’–βœ¨

ok so in every single example down here, whenever you see tab.fmt(), just know it's a CSVTabDef object bestie, that's the vibe πŸ’…


🌸 Defining Column Characteristics 🌸

these functions are THE moment for telling a column how it should look: its alignment, its width, and optionally its precision. the first argument is always the column's width, and the second one is optional and sets the column's precision (that's the one that matters when you're inserting floating point values). and listen β€” the precision only actually gets used if it's less than the column's width. no exceptions, that's the rule. πŸŽ€

  • FMT center(unsigned width, unsigned precision = ~0U) β€” when you insert this function's return value into tab.fmt(), everything you put in that column gets centered inside fields that are width characters wide. example:
        // values are centered in fields of 10
        // characters wide, floating point values
        // use 3 digit behind the decimal point:
tab.fmt() << center(10, 3);
  • FMT center(std::string const &str, unsigned precision = ~0U) β€” the cozy little convenience version that just calls str.length(), precision for you 🫢
  • FMT left(unsigned width, unsigned precision = ~0U) β€” insert this return value into tab.fmt() and the values in that column go hard left, left-aligned inside fields width characters wide. example:
        // values are left-aligned in fields
        // of 5 characters wide.
tab.fmt() << left(5);
  • FMT left(std::string const &str, unsigned precision = ~0U) β€” the convenience twin, calling left(str.length(), precision); 🌈
  • FMT right(unsigned width, unsigned precision = ~0U) β€” insert this return value into tab.fmt() and the column's values are right-aligned inside fields width characters wide. example:
        // values are right-aligned in fields
        // of 5 characters wide.
tab.fmt() << right(5);

alsooo, right-alignment is the default you get when you use CSVTab's fmt(std::string) member, or when you directly insert values into CSVTabDef objects. she's the default for a reason ✨

  • FMT right(std::string const &str, unsigned precision = ~0U) β€” yep, the convenience one, calling right(str.length(), precision). πŸ’…

πŸ’œ Inserting Table Elements πŸ’œ

in the examples below, tab is a CSVTable object that's returning a CSVTabIns object through its conversion operator. main character energy tbh πŸ‘‘

with one exception β€” hline β€” these functions are here to alter the column's default alignment and precision. again: the precision only gets used if it's less than the column's width. pass ~0U and the precision is just... ignored, not even invited. and if all you wanna do is overrule the default alignment? just inserting the matching FMT::Align value is enough, periodt.

πŸ¦‹ Altering the default alignment of individual columns

  • FMT precision β€” once you insert this function's return value into tab, the next value you insert gets centered, and precision is used when inserting floating point values.
        // centers 9.87 in column 1
tab << center(2) << 9.876";
  • FMT left(precision) β€” after inserting this return value into tab, the next value inserted is left-aligned, with precision used for floating point values.
        // left-aligns 9.87 in column 1
tab << left(2) << 9.876";
  • FMT right(precision) β€” insert this return value into tab and the next value inserted is right-aligned, precision used when inserting floating point values.
        // right-aligns 9.87 in column 1
tab << right(2) << 9.876";

fyi, CSVTable uses right-alignment by default. it's giving consistent πŸ’…

🧚 Joining columns

alignment specs can totally span multiple columns, and that's literally what the join functions are for. when you insert a value right after inserting the return value of a join member, that value occupies all the columns and uses the alignment type you named when calling join. and if things get spicy, the number of columns is reduced when necessary so you never exceed the table's last column. no overflow in this house 🚫

  • FMT join(unsigned nCols, FMT::Align align, unsigned precision = ~0U) β€” a value inserted into the table after join's return value occupies nCols columns, using alignment type align, and optionally using precision when inserting floating point values. the alignment specification must be FMT::CENTER, FMT::LEFT or FMT::RIGHT. example:
        // writes (assuming columns 2 and 3 occupy
        // 10 characters):
        //      left,    mid    , right
tab << "left" << join(2, FMT::CENTER) << "mid" << "right"";
  • FMT join(FMT::Align align, unsigned precision = ~0U) β€” same energy as the previous join, except this one occupies all the remaining columns of the table's current row (you can also pull this off by calling the first join function with ~0U as its first argument 🌟).

⭐ Inserting horizontal lines

if a single table element just needs a horizontal line, inserting Align::HLINE is all it takes, easy. but the hline functions are the ones for lines that span one or more table columns πŸŽ€

  • FMT hline(unsigned nCols = ~0U) β€” when you insert this function's return value into a CSVTabIns object, a horizontal line spanning nCols columns gets inserted into the table. if necessary, nCols is reduced so that the line doesn't run past the table's last column. and when it spans multiple columns, no column separators are used between the spanned columns β€” it's one single uninterrupted horizontal line. example:
        // columns 1 and 2: a horizontal line, column 3:
        // contains ’hi’ (preceded by the column separator)
tab << hline(2) << "hi";

πŸ”₯ EXAMPLE πŸ”₯

ok bestie this is the whole thing, look at her go:

#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();
}

and this program writes the following table to std::cout:

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

🌈 FILES 🌈

  • bobcat/csvtable β€” this is the one that defines the class interface πŸ’–

πŸ‘‘ SEE ALSO πŸ‘‘


πŸ’€ BUGS πŸ’€

None Reported. (she's flawless, no notes πŸ™ˆ)


πŸŽ€ BOBCAT PROJECT FILES πŸŽ€

Debian Bobcat project files:

  • libbobcat6: the debian package containing the shared library, changelog and copyright note;
  • libbobcat-dev: the debian package containing the static library, headers, manual pages, and developer info;

πŸ’œ BOBCAT πŸ’œ

Bobcat is an acronym of `Brokken’s Own Base Classes And Templates’. iconic name behavior honestly ✨


This is free software, distributed under the terms of the GNU General Public License (GPL).


πŸ’Œ AUTHOR πŸ’Œ

Frank B. Brokken (f.b.brokken@rug.nl).


and that's a wrap on the whole page bestie!! 🫢 this came from the Bobcat project β€” the version is libbobcat-dev_6.15.01, dated 2005-2026, and the page itself is FBB::CSVTable(3bobcat). go forth and make the prettiest little tables, you've absolutely got this πŸ’…πŸŒΈπŸ¦‹

πŸ“¦ where this page comes from