开发者

C++ string template library

开发者 https://www.devze.com 2022-12-24 23:15 出处:网络
I want simple C++ string based template library to replace strings at runtime. For example, I will use

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 exampl开发者_运维百科e, www.stringtemplate.org but I little scared when its talks about antlr etc.


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.


Can you use sprintf?

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


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");


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


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


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.


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


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.


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.


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.


I wrote my own library

+ you can set your delimiters

+ no need to initialize everything in place

+ you can decide which mode to use (be careful)

- already exists only std::string prototype

https://github.com/andriy-byte/template-string

0

精彩评论

暂无评论...
验证码 换一张
取 消