tags:

views:

205

answers:

2

Follow-up to post: http://stackoverflow.com/questions/2978096/using-width-precision-specifiers-with-boostformat

I'm trying to use boost::function to create a function that uses lambdas to format a string with boost::format. Ultimately what I'm trying to achieve is using width & precision specifiers for strings with format. boost::format does not support the use of the * width & precision specifiers, as indicated in the docs:

Width or precision set to asterisk (*) are used by printf to read this field from an argument. e.g. printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec); This class does not support this mechanism for now. so such precision or width fields are quietly ignored by the parsing.

so I'm trying to find other ways to accomplish the same goal.

Here is what I have so far, which isn't working:

#include <string>
#include <boost\function.hpp>
#include <boost\lambda\lambda.hpp>
#include <iostream>
#include <boost\format.hpp>
#include <iomanip>
#include <boost\bind.hpp>

int main()
{
 using namespace boost::lambda;
 using namespace std;

 boost::function<std::string(int, std::string)> f =
  (boost::format("%s") % boost::io::group(setw(_1*2), setprecision(_2*2), _3)).str();

 std::string s = (boost::format("%s") % f(15, "Hello")).str();

    return 0;
}

This generates many compiler errors:

1>------ Build started: Project: hacks, Configuration: Debug x64 ------
1>Compiling...
1>main.cpp
1>.\main.cpp(15) : error C2872: '_1' : ambiguous symbol
1>        could be 'D:\Program Files (x86)\boost\boost_1_42\boost/lambda/core.hpp(69) : boost::lambda::placeholder1_type &boost::lambda::`anonymous-namespace'::_1'
1>        or       'D:\Program Files (x86)\boost\boost_1_42\boost/bind/placeholders.hpp(43) : boost::arg<I> `anonymous-namespace'::_1'
1>        with
1>        [
1>            I=1
1>        ]
1>.\main.cpp(15) : error C2664: 'std::setw' : cannot convert parameter 1 from 'boost::lambda::placeholder1_type' to 'std::streamsize'
1>        No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>.\main.cpp(15) : error C2872: '_2' : ambiguous symbol
1>        could be 'D:\Program Files (x86)\boost\boost_1_42\boost/lambda/core.hpp(70) : boost::lambda::placeholder2_type &boost::lambda::`anonymous-namespace'::_2'
1>        or       'D:\Program Files (x86)\boost\boost_1_42\boost/bind/placeholders.hpp(44) : boost::arg<I> `anonymous-namespace'::_2'
1>        with
1>        [
1>            I=2
1>        ]
1>.\main.cpp(15) : error C2664: 'std::setprecision' : cannot convert parameter 1 from 'boost::lambda::placeholder2_type' to 'std::streamsize'
1>        No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>.\main.cpp(15) : error C2872: '_3' : ambiguous symbol
1>        could be 'D:\Program Files (x86)\boost\boost_1_42\boost/lambda/core.hpp(71) : boost::lambda::placeholder3_type &boost::lambda::`anonymous-namespace'::_3'
1>        or       'D:\Program Files (x86)\boost\boost_1_42\boost/bind/placeholders.hpp(45) : boost::arg<I> `anonymous-namespace'::_3'
1>        with
1>        [
1>            I=3
1>        ]
1>.\main.cpp(15) : error C2660: 'boost::io::group' : function does not take 3 arguments
1>.\main.cpp(15) : error C2228: left of '.str' must have class/struct/union
1>Build log was saved at "file://c:\Users\john\Documents\Visual Studio 2005\Projects\hacks\x64\Debug\BuildLog.htm"
1>hacks - 7 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

My fundamental understanding of boost's lambdas and functions is probably lacking. How can I get this to work?

+1  A: 

I think that, for this case, you would want to use boost.bind instead of boost.lambda. Part of the problem is that boost::io::group is a function template that takes and returns a variable number of objects, making it difficult to create the appropriate signature for the function<> declaration. I would create a string formatting function with a simple signature and then use boost.bind to create a specific formatting functor from that. i.e.

#include <string>
#include <iomanip>
#include <boost\function.hpp>
#include <boost\format.hpp>
#include <boost\bind.hpp>

using namespace boost;
using namespace std;

string fmt_str(const string& s, int w, int p)
{
    return (format("%s") % io::group(setw(w), setprecision(p), s)).str();
}

int main()
{
    function<string (int, string)> f = bind(fmt_str, _2, _1, _1);
    string s = f(15, "Hello");
    return 0;
}
Ferruccio
A: 

You should check the documentation of Boost.Lambda again and see what it is capable of and what not. For example, since the dot operator is not overloadable you can't invoke a member function like str() on a lambda expression like that. You need to use bind for this:

bind(&format::str, … )

This actually extends to all non-operator function calls as far as I can tell. As for creating a format object you need to defer the creation of it via something like this:

constructor<boost::format>(constant("%s"))  // untested

You see that with all the extra noise (bind, constructor, constant) you get a rather complex, long, and hard to decipher lambda expression. The best thing is probably to avoid it altogether and just use a simple function object

struct myfunctor {
    string operator()(int a, string b) const {
        return …
    }
};
…
void foo() {
    …
    boost::function<string(int, string)> f = myfunctor();
    …
}
sellibitze