Follow-up to post: Using * Width & Precision Specifiers With boost::format
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?
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;
}
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();
…
}
Related
I got some error messages when I try to compile cppTableMaker.cpp but it points to a library not create by me.
1>------ Build started: Project: cppTableMaker, Configuration: Debug Win32 ------
1>cppTableMaker.cpp
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.24.28314\include\xtree(1373,13): error C2675: unary '++': '_Iter' does not define this operator or a conversion to a type acceptable to the predefined operator
1> with
1> [
1> _Iter=std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
1> ]
1>G:\My Drive\coding\C++\cppTableMaker\cppTableMaker.cpp(31): message : see reference to function template instantiation 'void std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::insert<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>(_Iter,_Iter)' being compiled
1> with
1> [
1> _Kty=std::wstring,
1> _Ty=std::vector<std::wstring,std::allocator<std::wstring>>,
1> _Pr=std::less<std::wstring>,
1> _Alloc=std::allocator<std::pair<const std::wstring,std::vector<std::wstring,std::allocator<std::wstring>>>>,
1> _Iter=std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
1> ]
1>G:\My Drive\coding\C++\cppTableMaker\cppTableMaker.cpp(31): message : see reference to function template instantiation 'void std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::insert<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>>(_Iter,_Iter)' being compiled
1> with
1> [
1> _Kty=std::wstring,
1> _Ty=std::vector<std::wstring,std::allocator<std::wstring>>,
1> _Pr=std::less<std::wstring>,
1> _Alloc=std::allocator<std::pair<const std::wstring,std::vector<std::wstring,std::allocator<std::wstring>>>>,
1> _Iter=std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>>
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.24.28314\include\xtree(1374,36): error C2100: illegal indirection
1>G:\My Drive\coding\C++\cppTableMaker\cppTableMaker.cpp(183,1): fatal error C1903: unable to recover from previous error(s); stopping compilation
1>INTERNAL COMPILER ERROR in 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.24.28314\bin\HostX86\x86\CL.exe'
1> Please choose the Technical Support command on the Visual C++
1> Help menu, or open the Technical Support help file for more information
1>Done building project "cppTableMaker.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Seems like it comes from:
#include <string>
#include <vector>
#include <map>
#include "cppTableMaker.h"
#include "common.cpp"
#include <locale>
#include <codecvt>
#include <sstream>
//bunch of functions and methods, and the table::modernTable::column class. (there's no main())
std::vector<int> table::modernTable::ins(std::vector<std::wstring> values)
{
this->row ++;
auto local_data = this->data;
std::vector<int> local_colMaxLen = this->colMaxLen;
int loopCount{ 0 };
for (auto const& [key, val] : local_data)
{
local_data.insert(key, std::wstring(values.at(loopCount))); //there's some info pointing to the first column of this line.
local_colMaxLen.at(loopCount) = max(local_colMaxLen.at(loopCount), max(values.at(loopCount).length(), key.length()));
loopCount ++;
}
this->colMaxLen = local_colMaxLen;
this->data = local_data;
return local_colMaxLen;
}
included header file cppTableMaker.h:
#pragma once
#include <string>
#include <map>
#include <vector>
#ifndef cppTableMaker_h
#define cppTableMaker_h
namespace table {
class modernTable {
public:
//variables
std::map<std::wstring, std::vector<std::wstring>> data;
int row{ 0 };
std::vector<int> colMaxLen{};
//init
modernTable(std::map<std::wstring, std::vector<std::wstring>> data_INPUT, int row_INPUT, std::vector<int> colMaxLen_INPUT); //constructer
// ~modernTable(); //destructer
//methods
std::vector<int> ins(std::vector<std::wstring>); //this function might be the cause
std::wstring get();
std::vector<std::wstring> rm(int rowNum);
class column;
auto col(std::wstring name);
void mv(int index);
void mvto(int index);
};
//other classes
}
#endif
There's nothing special about common.cpp. It only contains a max() function.
So what is the problem of my script? What does it mean? I changed all ++ to +1 and the problem still exist. It just doesn't make sense that the error occurs in a standard library. Can I get rid of it? How and why?
map::insert want the iterator and value or std::pair.
if you relplace the problem row to pair, you code will be compile
//local_data.insert(key, std::wstring(values.at(loopCount))); //there's some info pointing to the first column of this line.
std::pair<std::wstring, std::vector<std::wstring>> value = std::make_pair(key, std::vector<std::wstring>{values.at(loopCount)});
local_data.insert(value); //there's some info pointing to the first column of this line.
But I think you want somesing like this:
val.push_back(values.at(loopCount)); //there's some info pointing to the first column of this line.
For do push into val, you should make non-const reference to for-variable
//for (auto const& [key, val] : local_data)
for (auto & [key, val] : local_data)
BTW if you will convert type of max argument from size_t to int, you can use std::max:
local_colMaxLen.at(loopCount) = std::max(local_colMaxLen.at(loopCount), int(std::max(values.at(loopCount).length(), key.length())));
Other way to use std::max is change type of counts from int to size_t.
The original problem is kind of long, so I simplify it here.
I need to create a group of strings, with a relevent integer, let's say a training group. Then I need to create many training groups. I want to manage all the training groups in a single container. So I decided to use boost::unordered_map<> with the key being std::unordered_set. Because, the BOOST has hash value for the standard C++ container.
The simplified code are as follows:
#include <string>
#include <unordered_set>
#include <utility>
#include<boost/unordered_map.hpp>
using namespace std;
int main()
{
boost::unordered_map< unordered_set<string>, int> training_groups;
pair<unordered_set<string>, int> a_training_group;
training_groups.insert(a_training_group);
return 0;
}
However, the code doesn't compile successfully. There are many cryptic warnings and an error. The error is as follows:
1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xhash(30): error C2440: 'type cast' : cannot convert from 'const std::unordered_set<std::string,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator<_Kty>>' to 'size_t'
1> with
1> [
1> _Kty=std::string
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> C:\Program Files\boost\boost_1_59_0\boost/functional/hash/extensions.hpp(262) : see reference to function template instantiation 'size_t stdext::hash_value<T>(const _Kty &)' being compiled
1> with
1> [
1> T=std::unordered_set<std::string,std::hash<std::string>,std::equal_to<std::string>,std::allocator<std::string>>
1> , _Kty=std::unordered_set<std::string,std::hash<std::string>,std::equal_to<std::string>,std::allocator<std::string>>
1> ]
I don't know where is the origin of this error and how to solve it. If the compiler cannot file the hash function of unordered_set, the error information will contains words like "Hash" or "Key". However, it just says something about type conversion, which looks similar to hash function. So, I feel confused.
Can anyone provide some advice. I am using Visual Studio 2013 on Windows 8.
PS: when I changed the Key unordered_set<string> to set<string> or vector<string>, the program compiles successfully. But I still don't know the reason, and don't know how to solve the problem if I am determined to use unordered_set<string> as the key.
Boost does not provide a hash function for std::unordered_set, the list of hash functions contains e.g. one for std::set:
http://www.boost.org/doc/libs/1_61_0/doc/html/hash/reference.html#idp6283424-bb
So you must provide your own hash function, which is relatively easy when using boost::hash_range:
#include <string>
#include <unordered_set>
#include <utility>
#include <boost/functional/hash/hash_fwd.hpp>
namespace boost
{
template <class K, class C, class A>
std::size_t hash_value(const std::unordered_set<K, C, A>& v)
{
return boost::hash_range(v.begin(), v.end());
}
} // namespace boost
#include <boost/functional/hash.hpp>
#include <boost/unordered_map.hpp>
int main()
{
boost::unordered_map<std::unordered_set<std::string>, int> training_groups;
std::pair<std::unordered_set<std::string>, int> a_training_group;
training_groups.insert(a_training_group);
return 0;
}
live example
I didn't really know what to write in the title, but basically I have a single .cpp, with only standard library headers included and no "using" keywords. I made my own "generate(...)" function. After including the library, Visual Studio shows me an error (where the function is being called), basically saying that it doesn't know whether to choose std::generate(...) or generate(...) because they have matching argument lists.
Is this a bug or have I missed something? I might also add that I am using VS2015.
#include <iostream>
#include <ctime>
#include <vector>
#include <algorithm>
template<typename Iter, typename Function>
Function generate(Iter begin, Iter end, Function f)
{
while (begin != end)
{
*begin = f();
++begin;
}
return f;
}
class Random
{
public:
Random(int low, int high)
: mLow(low), mHigh(high)
{}
int operator()()
{
return mLow + rand() % (mHigh - mLow + 1);
}
private:
int mLow;
int mHigh;
};
class Print
{
void operator()(int t)
{
std::cout << t << " ";
}
};
int main()
{
srand(time(0));
std::vector<int> intVec;
intVec.resize(15);
Random r(2, 7);
generate(intVec.begin(), intVec.end(), r);
}
Error output:
1>------ Build started: Project: Functor, Configuration: Debug Win32 ------
1> Main.cpp
1>c:\users\michael sund\documents\visual studio 2015\projects\gi_cpp\functor\main.cpp(44): warning C4244: 'argument': conversion from 'time_t' to 'unsigned int', possible loss of data
1>c:\users\michael sund\documents\visual studio 2015\projects\gi_cpp\functor\main.cpp(50): error C2668: 'generate': ambiguous call to overloaded function
1> c:\users\michael sund\documents\visual studio 2015\projects\gi_cpp\functor\main.cpp(7): note: could be 'Function generate<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>,Random>(Iter,Iter,Function)'
1> with
1> [
1> Function=Random,
1> Iter=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>
1> ]
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(1532): note: or 'void std::generate<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>,Random>(_FwdIt,_FwdIt,_Fn0)' [found using argument-dependent lookup]
1> with
1> [
1> _FwdIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>,
1> _Fn0=Random
1> ]
1> c:\users\michael sund\documents\visual studio 2015\projects\gi_cpp\functor\main.cpp(50): note: while trying to match the argument list '(std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>, std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>, Random)'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
This happens on not just VC++ (VS 2015), but g++ 4.9+ as well. The issue here is the tricky Argument Dependent Lookup (Koenig Lookup).
It looks at the two iterators you're adding and it sees the "generate" function in std because the iterators also come from the std namespace (this is the point of Argument Dependent Lookup).
This problem actually bit me at one point: when I wrote my own tie implementation that did a few things extra to tie. I had to call mine tye because Koenig Lookup caused the considered overloads to be equal in their ranking and thus cause an error like this.
Either prefix generate with :: to start lookup from the global namespace (::generate( vec.begin(), vec.end(), ... );), or name it differently.
I am using Visual Studio 2013.
I am using the following as part of my code:
#include <queue>
#include <curses> // pdcurses for mvprintw function
using namespace std;
typedef unsigned short ushort;
struct xy{
int x;
int y;
};
void move(ushort length, queue<xy>& test);
int main() {
// ...
}
void move(ushort length, queue<xy>& test) {
queue<xy> coord;
if (length <= test.size()) {
coord = test.pop();
mvprintw(coord.y, coord.x, " ");
}
// ...
}
If I were to use the queue I made (which does not allow for templates), setting it up to use that struct as its type, it works fine. However, I want to make use of a templated queue so I can use queues of other types as well. But when I use the c++ standard queue in the way given above, I get the following error:
error C2679: binary '=' : no operator found which takes a right-hand operand of type 'void' (or there is no acceptable conversion)
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\queue(101): could be 'std::queue<xy,std::deque<_Ty,std::allocator<_Ty>>> &std::queue<_Ty,std::deque<_Ty,std::allocator<_Ty>>>::operator =(std::queue<_Ty,std::deque<_Ty,std::allocator<_Ty>>> &&)'
1> with
1> [
1> _Ty=xy
1> ]
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\queue(43): or 'std::queue<xy,std::deque<_Ty,std::allocator<_Ty>>> &std::queue<_Ty,std::deque<_Ty,std::allocator<_Ty>>>::operator =(const std::queue<_Ty,std::deque<_Ty,std::allocator<_Ty>>> &)'
1> with
1> [
1> _Ty=xy
1> ]
1> while trying to match the argument list '(std::queue<xy,std::deque<_Ty,std::allocator<_Ty>>>, void)'
1> with
1> [
1> _Ty=xy
1> ]
Am I missing something simple? I don't see why it seems to think the pop function returns a void type. Does the queue not use pop() for what I think it does? Or is the error in how I am using the Queue in my code?
It looks like you meant to declare coord to be of type xy, not queue<xy>.
Popping from std::queue indeed returns nothing; it simply removes the front element. If you want the front element, call front and then pop.
coord = test.front();
test.pop();
Edit:
This has been reported as a VS2012 C++ compiler bug on Microsoft Connect (link).
Nov. 11, 2014: Microsoft has responded saying the fix for this bug should show up in the next major release of Visual C++.
I've been struggling with a VS2012 compiler error message I don't understand, so I trimmed down the problem to what seems like the bare minimum.
I'm building the following main.cpp using VS2012:
#include <utility>
template <typename T>
struct A
{
T x;
A(A&& other) : x(std::move(other.x)) { }
A(T&& x) : x(std::move(x)) { }
};
template <typename T>
A<T> build(T&& x)
{
return A<T>(std::move(x));
}
int main(int argc, char* argv[])
{
auto f = []()
{
return build([](){}); //error here
};
return 0;
}
The salient point is that I'm trying to use a lambda as the template type T of the build function. The error message I get is:
1> main.cpp
1>C:\test\main.cpp(21): error C2664: 'A<T>::A(A<T> &&)' : cannot convert parameter 1 from 'A<T>' to 'A<T> &&'
1> with
1> [
1> T=void (__cdecl *)(void)
1> ]
1> and
1> [
1> T=main::<lambda_c3c618d445b3cb24eede9bf304860ad7>::()::<lambda_4240e93016e3e420ff8383c9350ae130>
1> ]
1> and
1> [
1> T=void (__cdecl *)(void)
1> ]
1> Reason: cannot convert from 'A<T>' to 'A<T>'
1> with
1> [
1> T=main::<lambda_c3c618d445b3cb24eede9bf304860ad7>::()::<lambda_4240e93016e3e420ff8383c9350ae130>
1> ]
1> and
1> [
1> T=void (__cdecl *)(void)
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
I've done my research and looked up the page for the error message (link), but I still can't figure out what the problem is. Could you please explain this compiler error?
edit
Something is definitely weird here. If I change the code in main to look like this:
auto f = []()
{
int* n = new int(0);
auto g = [=](){ return *n; };
*n++;
return build<decltype(g)>(std::move(g));
};
I get an error message suggesting that T=int (__cdecl *)(void) in the call to build - which would mean that decltype(g) is giving me a function pointer? Huh? I'm capturing a pointer by value and then modifying it afterwards - shouldn't it have to create a functor - and one that has no cast to function pointer? Maybe I'm not understanding something.
See related: Lambda expressions : n3290 draft
Also, if this is a bug in the VS2012 compiler, can you think of a workaround?
I can confirm that using GCC (on linux), this code compiles just fine.
So I'd say that VisualStudio seems to be the source of the error.
I don't have Windows or Visual Studio to verify, nor do I have much experience with lambda functions in C++, but perhaps you need to include the (albeit empty) parameter list in the function? i.e. change line 21 to
return build([](){});
Both versions compile with GCC, but perhaps Visual Studio is a bit more picky.
The other question I might have is whether the lambda function you're defining at line 24 will work out since its return value involves the lambda function you're defining inside the function itself.
I do not know if that behavior comply with the standard but with VC++ 2019 that error happen only with the option /permissive-, then when the strict mode is on.
Nevertheless here is how to solve the problem, by just casting the lambda with a reference type:
template <typename FUNC>
void f(FUNC& o){}
int main()
{
f((std::function<void()>&)[](){});
// or also:
auto func = [](){};
f(func);
}