2

The signature of transform is:

OutputIterator transform (InputIterator first1, InputIterator last1,
                        OutputIterator result, UnaryOperation op);

And I want to create a generic token replacing functor, msg_parser, below, so I can use any container (string used in example below) and pass begin and end of container to transform. Thats the idea.

But I can't get this to compile.

Here is my code. Any help would be much appreciated.

#include <iostream>
#include <iterator>
#include <string>
#include <map>
#include <algorithm>


class msg_parser {
public:
   msg_parser(const std::map<std::string, std::string>& mapping, const char token = '$')
      : map_(mapping), token_(token) {}

   // I can use a generic istream type interface to handle the parsing.   
   std::ostream_iterator operator() (std::istream_iterator in) {
      //body will go through input and when get to end of input return output

   }

private:
   const char token_;
   const std::map<std::string, std::string>& map_;
};


int main(int argc, char* argv[]) {
   std::map<std::string, std::string> mapping;
   mapping["author"] = "Winston Churchill";
   std::string str_test("I am $(author)");
   std::string str_out;
   std::transform(str_test.begin(), str_test.end(), str_out.begin(), msg_parser(mapping));
   return 0;
}
Angus Comber
  • 9,316
  • 14
  • 59
  • 107
  • 1
    since you are acting on `std::string`s, your unary operator must act on `char`s. You also need a `std::back_inserter` for the output string. – juanchopanza Jun 11 '13 at 11:33

2 Answers2

3

Since std::string is a collection of chars, std::transform will iterate over chars exactly distance(first1, last1) times, so in your case it's not possible to change the size of the string. You may be able to transform "$(author)" into another string exactly the same size, though, but I guess it's not what you want.

You probably want to iterate over stream iterators instead of chars:

std::stringstream istrstr(str_test);
std::stringstream ostrstr;
std::transform(std::istream_iterator<std::string>(istrstr), 
               std::istream_iterator<std::string>(), 
               std::ostream_iterator<std::string>(ostrstr, " "), // note the delimiter 
               msg_parser(mapping));

std::cout << ostrstr.str() << std::endl;

By the way, your UnaryOperation works on the iterated type, not on iterators, so operator() should be:

std::string operator() (std::string in) { // ...
slaphappy
  • 6,894
  • 3
  • 34
  • 59
  • But the whole idea of this is to make it really generic. you just pass in an input iterator and it parses it sequentially because it is only an input iterator and outputs probably an output iterator. The input iterator could be an iterator from a string a vector, vector, a stream, etc etc – Angus Comber Jun 11 '13 at 12:14
  • The issue in your code is that there is a 1:1 relationship between the input and the output of `transform`: for each element in, one element out. Thus, *you can't iterate on chars*. If you want your algorithm to be compatible with char containers, you'll have to write a string iterator (like istream_iterator) that works on char containers. – slaphappy Jun 11 '13 at 12:26
  • One alternative would be to keep the stream iterators, but provide a stream wrapper for char collections: This has been done in this question http://stackoverflow.com/questions/8815164/c-wrapping-vectorchar-with-istream – slaphappy Jun 11 '13 at 12:28
  • I agree transform is the wrong choice for a parser. for_each would be better. I haven't found a real solution here but yours is best answer. – Angus Comber Jun 11 '13 at 15:11
1

You should read the documentations and examples for std::transform in a reference like this.

You'll notice that the operation shall take an element of the input container and generate an element for the output container. Since your containers are strings and the elements are chars, the signature should be char operator()(char). Container-iterators would be wrong in this case. Anyways, the iterators of std::string are char*s, so your std::ostream_iterator are completely senseless.

Having said that, you will notice that transform works on single characters, if you apply it to your string, not on the whole "author" substring. What you are trying to do is best achieved with C++11's std::regex regular expression library, not with std::transform

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90