How can I improve this template to accept lambdas in VS2012 - c++

The following is cute little template that I use often. Simply tells me if the given element is a member of a collection (which itself must be compatible with the find_if template):
// returns true if a given collection contains the given element
// NOTE: This is NOT optimized for associative containers!
template <typename ELEMENT, typename COLLECTION, typename PREDICATE>
bool contains(const COLLECTION & collection, ELEMENT element, PREDICATE predicate)
{
return collection.end() != std::find_if(collection.begin(), collection.end(), boost::bind(predicate, element, _1));
}
I'm finding that VC2012 balks if I try to use a lambda as the predicate:
if (!contains(specs, str, [] (CString pathname, CString pattern) { return AsBool(PathMatchSpec(pathname, pattern)); }))
continue;
VS2012SP1 spits out the following for the above context:
1>c:\users\steve\projects\cimex cad-cam\15.0\3rd party\boost\boost\bind\bind.hpp(69): error C2039: 'result_type' : is not a member of 'CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>'
1> c:\users\steve\projects\cimex cad-cam\15.0\cimex application\cimcad\macro directory.cpp(166) : see declaration of 'CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>'
1> c:\users\steve\projects\cimex cad-cam\15.0\3rd party\boost\boost\bind\bind_template.hpp(15) : see reference to class template instantiation 'boost::_bi::result_traits<R,F>' being compiled
1> with
1> [
1> R=boost::_bi::unspecified,
1> F=CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>
1> ]
1> c:\users\steve\projects\cimex cad-cam\15.0\mfc toolbox\miscellaneous.h(360) : see reference to class template instantiation 'boost::_bi::bind_t<R,F,L>' being compiled
1> with
1> [
1> R=boost::_bi::unspecified,
1> F=CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>,
1> L=boost::_bi::list2<boost::_bi::value<CString>,boost::arg<1>>
1> ]
1> c:\users\steve\projects\cimex cad-cam\15.0\cimex application\cimcad\macro directory.cpp(166) : see reference to function template instantiation 'bool contains<CString,substring_container_adapter,CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>>(const COLLECTION &,ELEMENT,PREDICATE)' being compiled
1> with
1> [
1> COLLECTION=substring_container_adapter,
1> ELEMENT=CString,
1> PREDICATE=CMacroInterpreter::GetDirectoryOf::<lambda_60eac39ee69a5bdc77e08d06d79ae4c4>
1> ]
I'm unclear on how to coerce things to accept the predicate lambda. Seems that boost is unable to deduce the return type of the lambda. And I'm unclear on what I can do to fix that?
I could define a local std::binary_function derivative functor. Just seems like it would be better to fix contains<> to allow it to handle lambdas directly.

It seems to be an issue with boost::bind. Using std::bind instead, your code builds fine with lambdas in VS2012:
#include <functional>
#include <algorithm>
#include <vector>
template <typename ELEMENT, typename COLLECTION, typename PREDICATE>
bool contains(const COLLECTION & collection, ELEMENT element, PREDICATE predicate)
{
return collection.end() != std::find_if(collection.begin(), collection.end(), std::bind(predicate, element, std::placeholders::_1));
}
std::vector<int> a;
int main()
{
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(42);
bool c = contains(a, 42, [](int a, int b) { return a == b; });
return 0;
}
The same code builds just fine with g++ as well.

You could always try using another lambda:
template <typename ELEMENT, typename COLLECTION, typename PREDICATE>
bool contains(const COLLECTION & collection, ELEMENT element, PREDICATE predicate)
{
typedef typename COLLECTION::value_type VALUE;
return collection.end() != std::find_if(collection.begin(), collection.end(),
[&]( VALUE const & e ){ return predicate( element, e ); });
}

Related

Cannot convert template when using lambda as parameter

I want to pass a lambda to a function, but I have run into a problem of successfully passing it onto the function. The function chooses to append TrueVal or FalseVal and creates a vector of boolean, based on the given condition.
I'm using 2019 Visual Studio's ISO C++14 Standard to compile the code.
#include <iostream>
#include <vector>
using namespace std;
template<typename T, typename T1, typename T2>
vector<bool> ConstructNestedVectorByElements(T condition, T1 TrueVal, T2 FalseVal) {
vector<bool> TempCol;
TempCol = {};
for (int i = 0; i < 3; i++)
{
if (condition(i)) {
TempCol.emplace_back(TrueVal);
}
else {
TempCol.emplace_back(FalseVal);
}
}
return TempCol;
}
int main()
{
vector<int> NumList = { 0, 1, 2 };
vector<bool> BoolList = {true, false, true};
auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, false); //this works
auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement); //error
return 0;
};
The error message:
C2440 'initializing': cannot convert from 'T2' to 'bool' ...\include\vector line 2385
1>...\vector(2385,18): error C2440: 'initializing': cannot convert from 'T2' to 'bool'
1> with
1> [
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
1>...\vector(2385,18): message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>...\Source.cpp(19): message : see reference to function template instantiation 'decltype(auto) std::vector<bool,std::allocator<bool>>::emplace_back<T2&>(T2 &)' being compiled
1> with
1> [
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
1>...\Source.cpp(36): message : see reference to function template instantiation 'std::vector<std::vector<bool,std::allocator<bool>>,std::allocator<std::vector<bool,std::allocator<bool>>>> ConstructNestedVectorByElements<main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,bool,main::<lambda_e116e485fb739b952327b9205614af81>>(T,T1,T2)' being compiled
1> with
1> [
1> T=main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,
1> T1=bool,
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
template <class... _Valty>
decltype(auto) emplace_back(_Valty&&... _Val) {
bool _Tmp(_STD forward<_Valty>(_Val)...);
push_back(_Tmp);
I think the problem might be one of the following:
I'm passing more than one type of argument into T2 (a lambda and a bool): Perhaps I used the wrong keyword, typename, to initialize T2? I tried with class but the same thing occurred.
OriginalElement isn't given parameters when it requires them: This confuses me a bit. If I change the line to:
TempCol.emplace_back(FalseVal(i, j)); //this is line 19
This error shows up:
C2064 term does not evaluate to a function taking 2 arguments ...\Source.cpp line 19
However, this seems not to be the case for condition(i, j), which compiles correctly. Is there a difference in handling (what I assume to be) boolean when in a conditional, and when appending it to a vector?
Lambdas not being constexpr, so it can't be used in templates: I don't really understand it, but there seems to be some relationship with this topic: (1, 2, 3)
The issue is that OriginalElement is not a bool and cannot be implicitly converted to one. You can call it to get a bool by passing an int. Change this line in the template:
TempCol.emplace_back(FalseVal(i));
then
auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement);
Compiles fine, but passing false will not work, because false is not a callable. T2 cannot be both, a callable and a bool. If you want both instantiations to work, you can change the first to
auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, [](int){ return false;});
Complete Demo
However, I suggest you to write two overloads. One that takes a callable. And to avoid the overhead of the callable, another one that takes a plain bool (no template parameter, let the conversion happen on the caller).

Template container with multiple template parameters interacting with other template containers with a different template parameter

Wordy title, yes, but I was unsure how else to say it. Suppose I have a container class which has two template parameters, the first of which is a type, the second of which is the size of the local storage for the container.
Now we have multiple containers with a different container storage size. Essentially, the container functions (all the public ones, anyway) only really care about T; N is only used to allocate local storage (an allocator is used if N is not enough).
I have put together a simple example implementation that showcases the problem I am having.
#include <iostream>
template <typename T, size_t N = 10>
class TestArray
{
public:
T Local[N];
class Iterator
{
public:
T* Array;
int Index;
Iterator() : Array(NULL), Index(-1) { }
Iterator(T* _array, int _index) : Array(_array), Index(_index) { }
bool operator == (const Iterator& _other) const
{
return _other.Index == Index && _other.Array == Array;
}
void Next() { ++Index; }
void Prev() { --Index; }
T& Get() { return Array[Index]; }
};
T& operator [] (const int _index) { return Local[_index]; }
Iterator Begin() { return Iterator(Local, 0); }
Iterator End() { return Iterator(Local, N); }
template <size_t _N>
void Copy(const TestArray<T, _N> &_other, int _index, int _count)
{
int i;
for (i = 0; i < _count; i++)
Local[_index + i] = _other[i];
}
};
This is really a two part question. I will concern this question only with the first part, and ask another regarding the second. I tried using it as follows:
int main() {
TestArray<int> testArray1;
TestArray<int, 25> testArray2;
TestArray<int>::Iterator itr1;
TestArray<int, 25>::Iterator itr2;
itr1 = testArray1.Begin();
for (itr1 = testArray1.Begin(); itr1 != testArray1.End(); itr1.Next())
{
itr1.Get() = itr1.Index;
}
testArray2.Copy(testArray1, 0, 10);
for (itr2 = testArray2.Begin(); itr2 != testArray2.End(); itr2.Next())
{
std::cout << itr2.Get() << std::endl;
}
return 0;
}
Here is an IDEONE link: http://ideone.com/1XKwD
When compiled with gcc-4.3.4, I get the following.
prog.cpp: In member function ‘void TestArray<T, N>::Copy(const TestArray<T, _N>&, int, int) [with unsigned int _N = 10u, T = int, unsigned int N = 25u]’:
prog.cpp:82: instantiated from here
prog.cpp:63: error: passing ‘const TestArray<int, 10u>’ as ‘this’ argument of ‘T& TestArray<T, N>::operator[](int) [with T = int, unsigned int N = 10u]’ discards qualifiers
When compiled with VS2010, I get the following.
1>------ Build started: Project: testunholytemplatemess, Configuration: Debug Win32 ------
1> main.cpp
1>c:\users\james\documents\testproj\testunholytemplatemess\testunholytemplatemess\main.cpp(63): error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const TestArray<T>' (or there is no acceptable conversion)
1> with
1> [
1> T=int
1> ]
1> c:\users\james\documents\testproj\testunholytemplatemess\testunholytemplatemess\main.cpp(44): could be 'int &TestArray<T>::operator [](const int)'
1> with
1> [
1> T=int
1> ]
1> while trying to match the argument list '(const TestArray<T>, int)'
1> with
1> [
1> T=int
1> ]
1> c:\users\james\documents\testproj\testunholytemplatemess\testunholytemplatemess\main.cpp(82) : see reference to function template instantiation 'void TestArray<T,N>::Copy<10>(const TestArray<T> &,int,int)' being compiled
1> with
1> [
1> T=int,
1> N=25
1> ]
Maybe I'm being thick, but I'm failing to interpret what either of these is actually trying to tell me (still somewhat new to templates). I also fail to understand why the operator [] method should really care about N, or the fact that I'm calling operator [] on a container with a different N value. If you change _other[i] to _other.Local[i], it works fine.
Does anyone have any suggestions?
You have to overload two versions for the []-operator, a const one and a non-const one:
T & operator [] (size_t _index) { return Local[_index]; }
const T & operator [] (size_t _index) const { return Local[_index]; }
Your constant Copy function is only allowed to use the second, constant version!

Is this a bug in Visual C++ 2010, or am I missing something?

Given the following code:
#include <vector>
template<class C1, class C2, class Op>
std::vector<typename Op::result_type>
f(Op op, const C1& src1, const C2& src2)
{
}
template<class It, class Op>
std::vector<typename Op::result_type> g(Op op, It begin, It end)
{
}
template<class It1, class It2, class Op>
std::vector<typename Op::result_type> g(Op op, It1 left_begin, It1 left_end, It2 right_begin)
{
return std::vector<typename Op::result_type>();
}
struct ToS
{
typedef double result_type;
double operator() (long , double ) const { return 0.0; }
};
std::vector<double> h(std::vector<long> const& vl, std::vector<double> const& vd)
{
return g(ToS(), vl.begin(), vl.end(), vd.begin());
}
When compiled with Visual C++ 2010 (SP1), I get the following errors:
1>VC10Error.cpp(30): error C2893: Failed to specialize function template 'std::vector<Op::result_type> g(Op,It1,It1,It2)'
1> With the following template arguments:
1> 'std::_Vector_const_iterator<_Myvec>'
1> with
1> [
1> _Myvec=std::_Vector_val<long,std::allocator<long>>
1> ]
1> 'std::_Vector_const_iterator<_Myvec>'
1> with
1> [
1> _Myvec=std::_Vector_val<double,std::allocator<double>>
1> ]
1> 'ToS'
1>VC10Error.cpp(30): error C2780: 'std::vector<Op::result_type> g(Op,It,It)' : expects 3 arguments - 4 provided
1> VC10Error.cpp(12) : see declaration of 'g'
I don't understand them. First, of course, the error message basically
sums up as "There's something wrong here, but we won't tell you what'.
And secondly, I don't find anything wrong; nor does g++ (version 4.4.2).
Other interesting symptoms: if you add a using std::vector; after the
include, and delete all of the std::, it works—I would have
thought that that should have no effect. And if you delete either the
function f (which really isn't used anywhere) or the first version of
function g, it also works.
So am I crazy, or is VC10 really not yet production-ready?
EDITED: Just to add: if it is a bug in the compiler, how do I reliably work around it?
Indeed it appears a bug in the compiler.
In your simplified example, the issue goes away if the two versions of g() exchange places, or if f() is commented out, or f() exchange places with g<It,Op>(Op, It, It).
It works fine with g++ compiler. So it's possible that VC++ is not able to parse through it properly (may be a bug in that case). Your code is proper.

HowTo create template of a container (e.x. std::map)?

So, I had a simple working code, for example
template <typename T, typename VALUE>
VALUE mapAt(T key)
{
std::map<T, VALUE>::const_iterator item_i(MyMap.find(key))
, end_i(MyMap.end());
if (item_i == end_i)
{
throw std::exception("No such key!");
}
return (*item_i).second;
}
Question: is it possible to create a new template function without using std::map, but using different containers (e.x. std::map, std::multimap, ...), something like this:
template <class Container, typename T, typename VALUE>
VALUE mapAt(Container& MyMap, T key)
{
Container<T, VALUE>::const_iterator item_i(MyMap.find(key))
, end_i(MyMap.end());
/* ... */
}
Problem: when i'm tring to use it, like:
std::map<int, char> my_map;
char ch = mapAt<std::map<int, char>(), int, char>(my_map, 123); // C2664 error
compiler gaves me an error:
main.cpp(119) : error C2664: 'mapAt' :
cannot convert parameter 1 from
'std::map<_Kty,_Ty>' to
'std::map<_Kty,Ty> (_cdecl &)' 1>
with 1> [ 1>
_Kty=int, 1> _Ty=char 1> ] 1> No user-defined-conversion
operator available that can perform
this conversion, or the operator
cannot be called
You can write something like this:
template <class Container>
typename Container::mapped_type mapAt(Container& MyMap, typename const Container::key_type& key)
{
typename Container::const_iterator iter = MyMap.find(key);
return iter->second;
}
int main()
{
std::map<int, char> my_map;
char ch = mapAt(my_map, 123);
}

C++: Error C2064 with STL

I'm trying to use STL, but the following doesn't compile. main.cpp:
#include <set>
#include <algorithm>
using namespace std;
class Odp
{
public:
set<int> nums;
bool IsOdd(int i)
{
return i % 2 != 0;
}
bool fAnyOddNums()
{
set<int>::iterator iter = find_if(nums.begin(), nums.end(), &Odp::IsOdd);
return iter != nums.end();
}
};
int main()
{
Odp o;
o.nums.insert(0);
o.nums.insert(1);
o.nums.insert(2);
}
The error is:
error C2064: term does not evaluate to a function taking 1 arguments
1> c:\program files\microsoft visual studio 10.0\vc\include\algorithm(95) : see reference to function template instantiation '_InIt std::_Find_if<std::_Tree_unchecked_const_iterator<_Mytree>,_Pr>(_InIt,_InIt,_Pr)' being compiled
1> with
1> [
1> _InIt=std::_Tree_unchecked_const_iterator<std::_Tree_val<std::_Tset_traits<int,std::less<int>,std::allocator<int>,false>>>,
1> _Mytree=std::_Tree_val<std::_Tset_traits<int,std::less<int>,std::allocator<int>,false>>,
1> _Pr=bool (__thiscall Odp::* )(int)
1> ]
1> main.cpp(20) : see reference to function template instantiation '_InIt std::find_if<std::_Tree_const_iterator<_Mytree>,bool(__thiscall Odp::* )(int)>(_InIt,_InIt,_Pr)' being compiled
1> with
1> [
1> _InIt=std::_Tree_const_iterator<std::_Tree_val<std::_Tset_traits<int,std::less<int>,std::allocator<int>,false>>>,
1> _Mytree=std::_Tree_val<std::_Tset_traits<int,std::less<int>,std::allocator<int>,false>>,
1> _Pr=bool (__thiscall Odp::* )(int)
1> ]
What am I doing wrong?
It needs to be declared static:
static bool IsOdd(int i)
Otherwise, you'd be asking find_if to call an instance method without an instance.
The problem is you are passing a pointer to member function. To call that function you would also need a pointer to this but the find_if doesn't let you to pass it. A solution is to wrap it using a function object, see Boost Bind (http://www.boost.org/doc/libs/1_43_0/libs/bind/bind.html) and Boost Function (http://www.boost.org/doc/libs/1_37_0/doc/html/function.html).
IsOdd does not use the class's internals in any way, so don't make it a member function. Instead, pull it out as a standalone function. Then you can call find_if with &IsOdd.
However, there is a benefit to taking things a step further and defining it as a function object:
#include <functional>
struct IsOdd : public unary_function<int, bool>
{
bool operator()(int i) const { return i % 2 != 0; }
};
Then calling find_if with IsOdd() will inline the code within the find_if loop instead of dereferencing a function pointer and making a function call.