16

I want simple C++ string based template library to replace strings at runtime.

For example, I will use

string template = "My name is {{name}}"; 

At runtime, I want the name to be changed based on actual one.

I found one example, www.stringtemplate.org but I little scared when its talks about antlr etc.

7
  • 4
    The 'templates' tag isn't appropriate here. Replacing strings within a template string isn't the same as the usual meaning of templates in a C++ context. Commented Mar 30, 2010 at 19:32
  • By 'actual one', you mean a variable called 'name' defined in the scope of the string template? Commented Mar 30, 2010 at 19:33
  • @Earwicker, It's still templating, technically. But something like string-formatting would work better. Commented Mar 30, 2010 at 19:33
  • 2
    template is not a valid identifier, it's a keyword. In any case, what is "actual one"? Can you give an example of your intentions? Commented Mar 30, 2010 at 19:37
  • Dare one ask where the 'name' in {{name}} is supposed to come from? Commented Mar 30, 2010 at 19:39

10 Answers 10

15

Update: The project has moved to Github and renamed into CTemplate: https://github.com/OlafvdSpek/ctemplate

From the new project page:

was originally called Google Templates, due to its origin as the template system used for Google search result pages. Now it has a more general name matching its community-owned nature.


Have you tried Google's CTemplate library ? It seems to be exactly what you are looking for: http://code.google.com/p/google-ctemplate/

Your example would be implemented like this:

In example.tpl:

My name is {{name}}

In example.cc:

#include <stdlib.h> #include <string> #include <iostream> #include <google/template.h> int main(int argc, char** argv) { google::TemplateDictionary dict("example"); dict.SetValue("name", "John Smith"); google::Template* tpl = google::Template::GetTemplate("example.tpl", google::DO_NOT_STRIP); std::string output; tpl->Expand(&output, &dict); std::cout << output; return 0; } 

Then:

$ gcc example.cc -lctemplate -pthread $ ./a.out 

My name is John Smith

Note that there is also a way to write templates as const strings if you don't want to bother writting your templates in separate files.

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

1 Comment

6

Can you use sprintf?

There's also boost::format if you want to include boost.

5 Comments

I'm suprised you're not getting death threats for even mentioning sprintf. But that's what I use, so +1 :)
snprintf to avoid buffer overruns. See libslack.org/manpages/snprintf.3.html
*death threat* @John: There, now this answer is complete.
@John Dibling, Sadly snprintf isn't standard C++. It is standard C99, though, and is an extension probably all major standard library implementations out there for C++ which also handle C99.
Neither of these solutions implements any lookup. The arguments must be in the same order as the replacements. There is no equivalent to {{name}}; there's just %s.
6

If you are new to C++, adding new libraries (template or otherwise) to your installation will only increase the learning curve. This is something you can do simply, elegantly, and efficiently with the built-in features.

Unlike similar answers, this code makes only one pass over the input and scales well with large dictionaries:

// header #include <map> #include <sstream> typedef std::map< std::string, std::string > subst_map; // implementation using namespace std; string do_substitutions( string const &in, subst_map const &subst ) { ostringstream out; size_t pos = 0; for (;;) { size_t subst_pos = in.find( "{{", pos ); size_t end_pos = in.find( "}}", subst_pos ); if ( end_pos == string::npos ) break; out.write( &* in.begin() + pos, subst_pos - pos ); subst_pos += strlen( "{{" ); subst_map::const_iterator subst_it = subst.find( in.substr( subst_pos, end_pos - subst_pos ) ); if ( subst_it == subst.end() ) throw runtime_error( "undefined substitution" ); out << subst_it->second; pos = end_pos + strlen( "}}" ); } out << in.substr( pos, string::npos ); return out.str(); } // usage pair< string, string > substitutions_init[] = { make_pair( "firstname", "homer" ), make_pair( "lastname", "simpson" ) }; subst_map substitutions ( substitutions_init, substitutions_init + sizeof(substitutions_init)/sizeof(*substitutions_init) ); int main() { cerr << do_substitutions( "Mr. {{lastname}}, {{firstname}} esquire", substitutions ) << endl; } 

Comments

4

If you have a function that replaces all occurrences of a string with another string:

std::string replace_all(std::string str, const std::string &remove, const std::string &insert) { std::string::size_type pos = 0; while ((pos = str.find(remove, pos)) != std::string::npos) { str.replace(pos, remove.size(), insert); pos++; } return str; } 

Then you can do this:

std::string pattern = "My name is {{first_name}} {{last_name}} and I live in {{location}}"; std::string str = replace_all(replace_all(replace_all(pattern, "{{first_name}}", "Homer"), "{{last_name}}", "Simpson"), "{{location}}", "Springfield"); 

2 Comments

just to be picky (very unlikely to happen in practice): if instead of Homer you put {{last_name}} as value of {{first_name}} you won't get the desired result...
@Andre Holzner - depends what the desired result is!
3

You can have a look at Inja. It is a simple, header-only template engine and does what you're asking for. There you can just call

data["name"] = "world"; inja::render("Hello {{ name }}!", data); // Returns "Hello world!" 

Comments

1

Have you considered a set of inline functions that use ostringstram instead of "string templates"?

inline std::string name_template(const std::string& name) { std::ostringstream os; os << "My name is " << name; return os.str(); } 

There are other alternate approaches if you need more generality. For example a class hierarchy where the base provides a "format" interface and child classes implement it with the appropriate varying implementation.

Comments

1

For whom is still looking for such a library I have created a little one https://github.com/lexxmark/string_template.

 stpl::string_template st("Hello {{name}}!"); st.set_arg("name", "World"); auto r = st.render(); EXPECT(r, "Hello World!"); 

It also has some nice customizations.

Comments

0
string skeleton = "My name is {{name}}"; string placeholder = "{{name}}"; string::size_type pos = skeleton.find(placeholder); while( pos != string::npos ) { skeleton.replace(pos, placeholder.length(), "Gopalakrishnan"); pos = skeleton.find(placeholder, ++pos); } 

1 Comment

If you have other tags then you can store then in a container and then wrap up the loop with another loop that traverses the container, thus applying the replace loop with each of the tags on the input string. Let me know if you have other tags and I'll update the sample code.
0

It might be overkill, but you can also take a look at boost::spirit, and more specifically, the 'karma' part which is a text generator.

Comments

0

If you have many place holders, e.g if you have a macro template for a letter you want to be automatically expanded, some basic tokenization would be easier to maintain, implement and extend later. E.g

//pseudocode foreach word in line { if word=={{name}} print getFromDB(name,entry) else if word=={{address}} print getFromDB(address,entry) .. .. else print word /* * to get rid of if-else-if tree, you can just check if starts with {{ and ends with }} and directly query the string against a db/map/hash */ } 

However, if the problem is a simple enough, and the template is small enough, just go for one of the answers mentioned above.

Comments