Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++.moderated > #2577

Re: C++11 Variadic Templates: Format("I %%% about %%% of %%%","dream", 7, 9) == "I dream about 7 of 9"

From Daniel Krügler <daniel.kruegler@googlemail.com>
Newsgroups comp.lang.c++.moderated, comp.lang.c++
Subject Re: C++11 Variadic Templates: Format("I %%% about %%% of %%%","dream", 7, 9) == "I dream about 7 of 9"
Date 2011-11-29 22:33 -0800
Organization A noiseless patient Spider
Message-ID <jb308c$bjo$1@dont-email.me> (permalink)
References <8ce7855d-ade8-4d83-ad8e-9fec2266c72b@b32g2000yqn.googlegroups.com>

Cross-posted to 2 groups.

Show all headers | View raw


On 2011-11-28 23:31, Andrew Tomazos wrote:
> I wanted to write a function that takes a string containing a number
> of placeholders and a corresponding number of other parameters
> (perhaps non-pod) and returns a string with the placeholders replaced
> with stringified versions of the parameters:

Yes, the idea is nice, but let me comment on some parts.

> I haven't used Variadic Templates before. My first draft is below. It
> appears to work, but it's using a linear recursion.  My question is,
> is there a way to write it iteratively?

Yes, see below.

> #include<string>
> #include<sstream>
> #include<iostream>
>
> using namespace std;
>
> class StringFormatException {};
>
> const string g_sPlaceholder("%%%");
>
> template<class ... T>
> inline string Format(const string&  sFormat, const T&  ...);

This function template is no-where use. You should get rid of it.

> template<class ... T>
> inline string Format(const string&  sFormat)

There is no reason to declare this as a variadic template. Just declare
it as a non-template function:

inline string Format(const string&  sFormat)

> {
>          size_t iFirstPlaceholderPos = sFormat.find(g_sPlaceholder);

Either use string::size_type or - more preferably auto to determine the
type of iFirstPlaceholderPos. In theory there is no guarantee that
size_t and string::size_type have the same type or even the same maximum
value (which is relevant when using string::npos).

>          if (iFirstPlaceholderPos != string::npos)
>                  throw StringFormatException();
>
>          return sFormat;
> }
>
> template<class Head, class ... Tail>
> inline string Format(const string&  sFormat, const Head&  head, const
> Tail&  ... tail)
> {
>          stringstream os;
>
>          size_t iFirstPlaceholderPos = sFormat.find(g_sPlaceholder);

Same problem here.

>          if (iFirstPlaceholderPos == string::npos)
>                  throw StringFormatException();
>          else
>          {
>                  string sFront(sFormat, 0, iFirstPlaceholderPos);
>                  string sBack(sFormat, iFirstPlaceholderPos +
> g_sPlaceholder.size());
>
>                  os<<  sFront<<  head<<  Format(sBack, tail ... );
>          }
>
>          return os.str();
> }
>
> int main()
> {
>          if (Format("I %%% about %%% of %%%","dream", 7, 9) == "I dream
> about 7 of 9")
>                  cout<<  "pass"<<  endl;
>          else
>                  cout<<  "fail"<<  endl;
>
>          return 0;
> }

In regard to your question, whether an iterative solution is possible:
Yes, at least in the sense that you can serialize your expansions. The
following code demonstrates this:

//----------------------------
#include <string>
#include <sstream>
#include <iostream>
#include <cstddef>

class StringFormatException {};

const std::string g_sPlaceholder("%%%");

struct void_{};

template<class T>
void_ DoFormat(std::ostream& os, const std::string& sFormat,
              std::string::size_type& curr, const T& t)
{
  auto nextPos = sFormat.find(g_sPlaceholder, curr);
  if (nextPos == std::string::npos)
    throw StringFormatException();
  else
  {
    os.write(&sFormat.front() + curr, nextPos - curr);
    os << t;
    curr = nextPos + g_sPlaceholder.size();
  }
  return {};
}

template <class... Args>
inline std::string Format(const std::string& sFormat, const Args&... args)
{
  std::stringstream os;
  std::string::size_type curr = 0;
  std::initializer_list<void_>{DoFormat(os, sFormat, curr, args)...};
  auto nextPos = sFormat.find(g_sPlaceholder, curr);
  if (nextPos != std::string::npos)
    throw StringFormatException();
  if (!sFormat.empty())
    os.write(&sFormat.front() + curr, sFormat.size() - curr);
  return os.str();
}

// ... Your test main() follows here unchanged
//----------------------------

You may wonder about the astonishing void_ type as well as the
std::initializer_list<void_>{DoFormat(os, sFormat, curr, args)...}
expression.

What I'm realizing here is to create an initializer-list expanded with
sizeof...(Args) expressions that have type void_. Unfortunately I cannot
use void here, because this is no object type. Even more unfortunate is,
that I cannot use a function call e.g.

template<class... T>
void swallow(T&&...){}

...

swallow(DoFormat(os, sFormat, curr, args)...);

This will compile, but since the order of the evaluation of function
arguments is not determined, there is no guarantee that this works in a
portable manner, because each function call modifies the arguments os
and curr. There exists a special rule in C++11 that ensures that
initializer-lists are evaluated in a strict order, this is the reason
why I'm creating an otherwise unused std::initializer_list<void_> here.

As elegant as it looks, I'm very unhappy about the need to create such a
hack here. It would be great, if there would exist a further expansion
pattern that allows me to write e.g.

DoFormat(os, sFormat, curr, args)...;

such that DoFormat may have type void as well.

I hope nonetheless that this suggestion gives you another idea how to
proceed.

HTH & Greetings from Bremen,

Daniel Krügler


-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

Back to comp.lang.c++.moderated | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

C++11 Variadic Templates: Format("I %%% about %%% of %%%","dream", 7,  9) == "I dream about 7 of 9" Andrew Tomazos <andrew@tomazos.com> - 2011-11-28 14:31 -0800
  Re: C++11 Variadic Templates: Format("I %%% about %%% of  %%%","dream", 7,  9) == "I dream about 7 of 9" legalize+jeeves@mail.xmission.com (Richard) - 2011-11-28 22:58 -0800
    Re: C++11 Variadic Templates: Format("I %%% about %%% of  %%%","dream", 7,  9) == "I dream about 7 of 9" "news" <newsmaster@tdcnet.fi> - 2011-11-29 22:24 -0800
      Re: C++11 Variadic Templates: Format("I %%% about %%% of  %%%","dream", 7,  9) == "I dream about 7 of 9" legalize+jeeves@mail.xmission.com (Richard) - 2011-11-30 12:45 -0800
    Re: C++11 Variadic Templates: Format("I %%% about %%% of  %%%","dream", 7, 9) == "I dream about 7 of 9" Andrew Tomazos <andrew@tomazos.com> - 2011-11-29 22:26 -0800
  Re: C++11 Variadic Templates: Format("I %%% about %%% of %%%","dream", 7,  9) == "I dream about 7 of 9" Daniel Krügler <daniel.kruegler@googlemail.com> - 2011-11-29 22:33 -0800
    size_type and size_t Seungbeom Kim <musiphil@bawi.org> - 2011-12-02 05:54 -0800
      Re: size_type and size_t Jean-Marc Bourguet <jm@bourguet.org> - 2011-12-02 22:33 -0800
      Re: size_type and size_t Daniel Krügler <daniel.kruegler@googlemail.com> - 2011-12-02 22:35 -0800

csiph-web