Reputation: 617
Edit: Compare
was a given parameter class type, so I have created struct Compare
, but i don't know how to cast the lambda
variable passed in test to Compare
class, which is a wanted parameter type.
Edit2: Alright guys, I think I sounded pretty unclear, so I'm posting the whole assignment text.
Create clamp function with two overloads
T const & clamp (T const & value, T const & low, T const & high) T const & clamp (T const & value, T const & low, T const & high, Compare cmp)
which returns
low
ifvalue
is less thanlow
,high
ifvalue
is greater thanhigh
, and otherwise value. In other words, value is limited to the range[low, high]
. The first overload assumes that T is comparable using the<operator
, the second>overload
then takes a suitable comparator as the 4th argument.
Unit test for the second overload is this:
TEST_CASE("Clamping using a custom lambda", "[all]") {
const int low = 255;
const int high = 0;
auto cmp = [](int lhs, int rhs) { return std::abs(lhs) > std::abs(rhs); };
int value, expected;
std::tie(value, expected) = GENERATE(values<std::pair<int, int>>({
{-1, -1},
{256, 255},
{-256, 255},
{244, 244},
{0, 0},
{-22, -22},
{-255, -255},
{255, 255}
}));
REQUIRE(clamp(value, low, high, cmp) == expected);
}
Now, I am just making and submitting header
file. And I don't know how to cast the 4th parameter, which is a lambda function, to a Compare
that from assignment looks like a struct
that I have to implement.
Remember, I cannot change the function declaration.
Upvotes: 1
Views: 1233
Reputation: 98425
Compare
can a generic type. Then it won't matter what's passed in - as long as it's invokable, it'll work:
#include <functional>
template <tyepname T, class Compare>
T const & clamp (T const & value, T const & low, T const & high, Compare greater) {
if (std::invoke(greater, value, high))) return high;
if (std::invoke(greater, value, low))) return value;
return low;
}
Also note that in C++ the argument names are not relevant in declarations and are ignored there, i.e. the declaration can use cryptic argument names, or even none at all. Now note that cmp
here is not something that anyone should be teaching: it's impossible to tell at a glance what it's supposed to do. There's no need to write a comment about it. The name of the argument should be, obviously, greater
. Hereby I apologize for all the times I wrote cmp
instead of less
or greater
or equals
- I'm sure some of this inertia has tainted my answers on SO. Mea culpa, and it was a stupid thing to do, I admit :)
So it's perfectly fine to have the declaration unchanged as per requirements, but still use argument names that don't make your blood boil in the definition:
// declaration
template <typename T, class Compare>
T const & clamp (T const & value, T const & low, T const & high, Compare cmp);
// definition
template <typename T, class Compare>
T const &clamp(T const &value, T const &low, T const &high, Compare greater);
I also find it rather confusing that the declarations offered in the assignment refer to some T
, but it's not specified whether T
is a constant type, or a generic type (i.e. a type parameter to a template).
You have to read the assignment carefully and divine whether T
is something that is a constant type provided to you, but not specified in the assignment itself (i.e. a requirement imposed on you), or whether T
means that your code must accept any type that will work in these circumstances. If that's not clear - ask the professor, because this sort of stuff is so fundamental that I'm baffled it wouldn't be clearly stated. My expectation is that you'd have included this ever-important detail were it provided in the assignment!
It is possible to "solve" the assignment in three circumstances that may arise - you need to determine what is expected of you, and if it takes you longer than 15 minutes to figure it all, absolutely, positively ask the instructor.
In other words, the test environment defines an arbitrary T
and Compare
, i.e. that they are fixed types with those names in global scope, then your functions won't be function templates - and in this case you do not care what T
nor Compare
is - they must be "well behaved" enough types to be usable, i.e. that a reasonable solution to the assignment exists. To write such code, make sensible assumptions to get it to compile, and then remove the definitions - since they'd clash with what the test environment provides.
// remove before running tests - this would be provided by test code
using T = int;
using Compare = bool(*)(T, T);
// end remove before running tests
T const & clamp (T const & value, T const & low, T const & high, Compare greater) {
// your implementation here
}
For example, the test case you have shown implies that:
T = int
, and Compare = bool(*)(int, int)
.
In that case, I'd still use the aliases T
and Compare
, but just set them to those determined types:
// do not remove those
using T = int;
using Compare = bool(*)(int, int);
T const & clamp (T const & value, T const & low, T const & high, Compare greater) {
// your code here
}
Then you need to make your functions generic - i.e. so that they also take the types as (template) arguments:
template <typename T, typename Compare>
T const & clamp (T const & value, T const & low, T const & high, Compare greater)
{ /* your code here */ }
In template
as used above, the words typename
and class
are synonyms. But only as used above - that's not always the case.
Upvotes: 1
Reputation: 595622
There is nothing in the posted assignment that says Compare
is expected to be a struct/class type that you have to implement.
There are only 3 ways to pass a lambda in a function parameter:
if the parameter is a plain function pointer, and the lambda is non-capturing.
if the parameter is defined as a template type.
if the parameter is defined as a std::function
.
So, unless Compare
in your case is defined as an alias for either int(*)(int,int)
or std::function<int(int,int)>
, then Compare
should be a template parameter instead. That will allow the caller to decide what type it wants to use for the cmp
callback. Compare
should not be an actual class type of its own.
For example:
template<typename T, typename Compare>
T const& clamp(T const& value, T const& low, T const& high, Compare cmp)
This would allow the caller to pass in anything that clamp()
can call using cmp(param1, param2)
- which can include:
int compareInts(int lhs, int rhs) { return std::abs(lhs) > std::abs(rhs); }
REQUIRE(clamp(value, low, high, compareInts) == expected);
std::bind()
'ed class method:class CompareInts {
public:
int compare(int lhs, int rhs) { return std::abs(lhs) > std::abs(rhs); }
};
using namespace std::placeholders;
CompareInts obj;
auto cmp = std::bind(&CompareInts::compare, &obj, _1, _2);
REQUIRE(clamp(value, low, high, cmp) == expected);
operator()
:struct CompareInts {
int operator()(int lhs, int rhs) { return std::abs(lhs) > std::abs(rhs); }
};
REQUIRE(clamp(value, low, high, CompareInts{}) == expected);
auto cmp = [](int lhs, int rhs) { return std::abs(lhs) > std::abs(rhs); };
REQUIRE(clamp(value, low, high, cmp) == expected);
Upvotes: 3