C++11 Variadic Templates implementation of parameterized strings

C++11 introduces variadic templates that allow nice way to have generic functions with unknown arguments, especially when used in recursive way. This makes easy to implement functions that before were cumbersome to write or hard to use.

Nice use case are parametric formatted strings where part of the strings are replaced with run time data. For example:

"Hello {0}, how are you?"
"Hello {name}, you logged in last {date}".

This find of format is quite easy to implement with recursive variadic templates so that it supports both indexed  and named replacement.

inline std::string format_r(int /*pos*/, std::string format) { return format; }
inline std::string format(const std::string format) { return format; }

template <typename T, typename... ARGS>
std::string format_r(int pos, std::string format, const T&amp; value, ARGS... args);

// convenience short hand for building a format parameter
template <typename... ARGS>
std::pair<ARGS...> _p(ARGS... args) { return std::make_pair(std::forward<ARGS>(args)...); };

template <typename K, typename T, typename... ARGS>
std::string format_r(int pos, std::string format, const std::pair<K, T>& value, ARGS... args)
{
    std::ostringstream os;
    os << value.second;
    auto parameter = str("{", value.first, "}");
    replace_all(format, parameter, std::string(os.str()));
    return format_r(pos + 1, format, std::forward<ARGS>(args)...);
}

template <typename T, typename... ARGS>
std::string format_r(int pos, std::string format, const &T value, ARGS... args)
{
    return format_r(pos, format, std::make_pair(str(pos), value), std::forward<ARGS>(args)...);
}

template <typename T, typename... ARGS>
std::string format(const std::string format, const &T value, ARGS... args)
{
    return format_r(0, format, value, std::forward<ARGS>(args)...);
}

 

Format function wraps a recursive function that builds the string by replacing one parameter at a time and then processing it again for the next argument until it runs out of parameters.

String conversions are handled with std::ostringstream that supports overloaded ‘<<‘ operation for most common data types.

Format function can be called with index formatting:

using util::format;
format("Hello {0}!", "World"); // Hello World
format("{0} {1}!", "Hello", "World"); // Hello World
format("{0} + {1} = {2}", 1, 2, 3); // 1 + 2 = 3

Or alternatively with explicit parameter names:

using util;
format("Hello {what}!", _p("what", "World"));
format("{word} {other}!", _p("word", "World"), _p("other", "World"));
format("{a} + {b} = {c}", _p("a", 1), _p("b", 2), _p("c", 3));

Get full implementation ftom https://github.com/tikonen/blog/tree/master/variadicformat

Quick and robust C++ CSV reader with boost

This is quick and simple CSV reader based on Boost regular expression token iterator. Parser splits the input with a regular expressions and returns the result as a collection of vectors of strings.
Regular expression handles neatly lot of the complicated edge cases such as empty columns, quoted text, etc..

Parser code

#include <boost/regex.hpp>

// used to split the file in lines
const boost::regex linesregx("\\r\\n|\\n\\r|\\n|\\r");

// used to split each line to tokens, assuming ',' as column separator
const boost::regex fieldsregx(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");

typedef std::vector<std::string> Row;

std::vector<Row> parse(const char* data, unsigned int length)
{
    std::vector<Row> result;

    // iterator splits data to lines
    boost::cregex_token_iterator li(data, data + length, linesregx, -1);
    boost::cregex_token_iterator end;

    while (li != end) {
        std::string line = li->str();
        ++li;

        // Split line to tokens
        boost::sregex_token_iterator ti(line.begin(), line.end(), fieldsregx, -1);
        boost::sregex_token_iterator end2;

        std::vector<std::string> row;
        while (ti != end2) {
            std::string token = ti->str();
            ++ti;
            row.push_back(token);
        }
        if (line.back() == ',') {
            // last character was a separator
            row.push_back("");
        }
        result.push_back(row);
    }
    return result;
}

Example

CSV data with common problem cases, such as empty quotes, commas inside quotes and empty last column.

a,b,c
1,"cat",3
",2",dog,4
3,a b,5
4,empty,
5,,empty
6,"",empty2
7,x,long story no commas
8,y,"some, commas, here,"

Read and parse the CSV data above and output the parsed result

int main(int argc, char*argv[])
{
	// read example file
	std::ifstream infile;
	infile.open("example.csv");
	char buffer[1024];
	infile.read(buffer, sizeof(buffer));
	buffer[infile.tellg()] = '\0';

	// parse file
	std::vector<Row> result  = parse(buffer, strlen(buffer));

	// print out result
	for(size_t r=0; r < result.size(); r++) {
		Row& row = result[r];
		for(size_t c=0; c < row.size() - 1; c++) {
			std::cout << row[c] << "\t";
		}
		std::cout << row.back() << std::endl;
	}
}

Output

$ ./reader
a      	b      	c
1      	"cat"  	3
",2"   	dog    	4
3      	a b    	5
4      	empty
5      	        empty
6      	""      empty2
7      	x      	long story no commas
8      	y      	"some, commas, here,"

See full example code in Github: https://github.com/tikonen/blog/tree/master/boostcsvreader