1

I have written a template file which is as follows

Hello ${Name} I like ${food} 

I wanted to write a c++ code which generates the following code using the template file as reference

Hello John I like Pasta I like Pasta I like Pasta 

Is there a way to do this in C++? I came across "ctemplate", but I was not convinced. The application I am developing is cross-platform. ( I wanted to do something like string template in c#)

6
  • If I understood well, you need some way to parse ${xxxx}. If your compiler supports C++11 use the new powerful regular expressions library. It does almost whatever parsing someone would need. cplusplus.com/reference/regex Commented May 7, 2014 at 10:31
  • I am using GNU-GCC compiler Commented May 7, 2014 at 10:34
  • You are ok if your version is > 4.6.1. Commented May 7, 2014 at 10:40
  • I wanted a cleaner way of doing that like in c#. Commented May 7, 2014 at 10:47
  • I don't know much of C#, but in C++11 the cleanest way to parse is regular expressions. Commented May 7, 2014 at 10:49

2 Answers 2

2

I've written a template expansion 'engine' using Boost Spirit before:

It's really versatile

  • supports nested expansions
  • supports recursive expansions
  • supports dynamic expansions (e.g. if you want a variable to be expanded with a different value depending on the context)

I've just adapted it to your question's macro syntax. See it Live On Coliru


Update

Okay, since performance appears to be the primary goal, here's a highly optimized expansion engine, in a benchmark:

#include <string> #include <sstream> #include <map> #include <boost/utility/string_ref.hpp> template <typename Range> std::string expand(Range const& key) { if (key == "Name") return "John"; if (key == "food") return "Pasta"; return "??"; } #include <iostream> int main() { static const std::string msg_template = "Hello ${Name}\n" "I like ${food}\n" ; std::ostringstream builder; builder.str().reserve(1024); // reserve ample room, not crucial since we reuse it anyways for (size_t iterations = 1ul << 22; iterations; --iterations) { builder.str(""); std::ostreambuf_iterator<char> out(builder); for(auto f(msg_template.begin()), l(msg_template.end()); f != l;) { switch(*f) { case '$' : { if (++f==l || *f!='{') { *out++ = '$'; break; } else { auto s = ++f; size_t n = 0; while (f!=l && *f != '}') ++f, ++n; // key is [s,f] now builder << expand(boost::string_ref(&*s, n)); if (f!=l) ++f; // skip '}' } } default: *out++ = *f++; } } // to make it slow, uncomment: // std::cout << builder.str(); } std::cout << builder.str(); } 

It runs 2^22 (4,194,304) iterations in ~0.775s

See it Live On Coliru too (where it runs in ~1.8s).

Sign up to request clarification or add additional context in comments.

2 Comments

See here for a similar task, solved without Boost Spirit, too; stackoverflow.com/a/17128601/85371
Added a streaming solution without boost that's bleedingly fast.
0

The standard libraries have excellent facilities for everyday regex parsing (which is what you need), take a look at the docs here.

You need to learn about regex if you've never heard of it - this is at least one place outlining the details.

Alternately if you are concerned with performance and your task is literally as simple as you describe then writing your own parser should be very straight forward using two streams and seeking forward for the ${ escape sequence while copying across to the output stream substituting as needed.

2 Comments

There will be a performance issue with regex. Say I have to write "I like Pasta" a million times, it will be an issue.
@AdityaBhat My solution works completely in streaming mode (except for the value of the macro itself, although that could be arranged as well)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.