std::is_base_of and error with if - c++

I've got problem with templates:
I have got two constructors and method:
.cpp:
Cell::Cell(sf::Vector2i& uPos, sf::Vector2f& cellDimensions, std::string& stateName)
:unitPosition(uPos)
{
setBasicParameters<std::string>(stateName,cellDimensions,uPos);
}
Cell::Cell(sf::Vector2i & uPos, sf::Vector2f & cellDimensions, int stateNumber)
:unitPosition(uPos)
{
setBasicParameters<int>(stateNumber,cellDimensions,uPos);
}
.hpp::
//Basic parameters which are being used by constructors
template < typename T = typename std::enable_if< std::is_base_of<int, T>::value, T>::type || typename std::enable_if< std::is_base_of<std::string, T>::value, T>::type>
void setBasicParameters(T& object, sf::Vector2f& cellDimensions, sf::Vector2i& uPos);
template<typename T>
inline void Cell::setBasicParameters(T& object, sf::Vector2f& cellDimensions, sf::Vector2i& uPos)
{
shape.setSize(cellDimensions);
shape.setOutlineThickness(cellDimensions.x / 10.0f); //10%
shape.setOutlineColor(constants::cell::FILL_COLOR);
shape.setPosition(uPos.x*cellDimensions.x, uPos.y*cellDimensions.y);
if (!StateSystem::isStateExist(object))
{
Logger::Log(constants::error::stateSystem::STATE_DOES_NOT_EXIST, Logger::STREAM::BOTH, Logger::TYPE::ERROR);
state = StateSystem::getNumberOfState(constants::defaults::EMPTY);
}
else
{
if (std::is_base_of<std::string, T>::value)
state = StateSystem::getNumberOfState(object);
else state = object;
setColor(StateSystem::getColorOfState(state));
}
}
and problem is there:
if (std::is_base_of<std::string, T>::value)
state = StateSystem::getNumberOfState(object);
else state = object;
In this if, I check a type of T, and if it is std::string, I use method from StateSystem which changes name to number. In the other way, if T is int, I don't need to change it so I am immediately assign T to state(state is int). But my compiler checks the dwo options and gives me errors:
Severity Code Description Project File Line Suppression State
Error C2440 '=': cannot convert from 'std::string' to 'uint8_t'
Severity Code Description Project File Line Suppression State
Error C2664 'int8_t mv::StateSystem::getNumberOfState(std::string)': cannot convert argument 1 from 'int' to 'std::string'
Can I repair it without do two diffrent methods?

The problem is that the if statement here...
if (std::is_base_of<std::string, T>::value)
...is a run-time branch. Even though is_base_of can be evaluated at compile-time, the compiler is forced to compile both branches of the if statement, even if their correctness relies on the is_base_of condition.
Can I repair it without two different methods?
C++17 introduces if constexpr (...), which does the branching at compile-time. This still requires both branches to be parseable, but only instantiates the one that matches the predicate. Therefore the non-taken branch can be "invalid" and your program will work as expected.
if constexpr (std::is_base_of<std::string, T>::value)
state = StateSystem::getNumberOfState(object);
else state = object;
If you do not have access to C++14 and you really don't want to use two different functions, you can implement an equivalent construct to if constexpr(...). The implementation requires a significant amount of boilerplate. The final result will look like this:
static_if(std::is_base_of<std::string, T>{})
.then([&](auto){ state = StateSystem::getNumberOfState(object); })
.else_([&](auto){ state = object; })(_);
I gave a talk at CppCon 2016 and Meeting C++ 2016 called "Implementing static control flow in C++14" which explains how static_if works and how to implement it yourself.
If you decide that using two different functions is acceptable, here's how you can solve the issue:
if (!StateSystem::isStateExist(object))
{
// ...as before...
}
else
{
dispatch(state, std::is_base_of<std::string, T>{});
// ...as before...
}
Where dispatch is defined as:
void dispatch(State& state, Object& object, std::true_type)
{
state = StateSystem::getNumberOfState(object);
}
void dispatch(State& state, Object& object, std::false_type)
{
state = object;
}

std::is_base_of is a compile-time thing. You should use it with static_assert, for example. Type information at runtime in C++ is something complicated and you should use stuff like RTTI or dynamic_cast for it.
This answer gives answers about the difference.

Related

Cleanly handling void as generic return type of labmda in C++

I am trying to write a function like this:
template <typename T>
void testActionBehavesIdentically(Foo& fooA, Foo& fooB, std::function<T(Foo&)> action)
if (std::is_same<T, void>::value)
{
// perform action even if T is void
action(fooA);
action(fooB);
}
else
{
// if T is not void, test also return values
T resultA = action(fooA);
T resultB = action(fooB);
CHECK_EQUAL(resultA, resultB);
}
// tests that the state of both foos is the same
CHECK_EQUAL(fooA, fooB);
}
where T is sometimes void. The compilation (on VS2019) fails with
error C2182: 'resultA': illegal use of type 'void'. Is there a clean way around it? (Hopefully one that will compile in most standard compilers)? Thank you.
Right now what's happening is that the compiler still will compile both if branches (since it doesn't actually know which will be called until runtime). This results in a failure since one of the branches doesn't compile correctly. There are a couple fixes (from the comments):
If your compiler supports it this would be one option:
if constexpr (std::is_same<T, void>::value) {
// perform action even if T is void
action(fooA);
action(fooB);
} else {
...
}
this will only actually compile one branch depending on the type of T.
If your compiler doesn't support it, here's another option:
template <typename T>
void testActionBehavesIdentically(Foo& fooA, Foo& fooB, std::function<T(Foo&)> action) {
// check assuming T != void
}
// overload which will be called if T was void
void testActionBehavesIdentically(Foo& fooA, Foo& fooB, std::function<void(Foo&)> action) {
// check assuming T == void
}
The compiler will match the overload when T is void instead of dispatching to the generic version of the function. Here you could even say "if T is an int do something different" which is kind of neat (but can get messy).

How to implement a function that safely cast any larger type to a smaller type in C++ using templates?

I'm trying to write a function that checks if the variable being cast can fit in the destination type, and assert() if not. Right now this is what I came up with. I didn't test it yet. I would like to make the template figure out the type of the varible being passed automatically, with something like typeid, although I don't really know what typeid really is. Is that possible? Also, I don't know much about templates.
template<typename from_T, typename to_T>
static inline to_T safe_cast(from_T variable)
{
assert(variable >= std::numeric_limits<to_T>::min());
assert(variable <= std::numeric_limits<to_T>::max());
return static_cast<to_T>(variable);
}
Well, if that is actually some function that already does this that I don't know of I will be glad to hear.
C++ Core Guidelines already has a gsl::narrow
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
You can see the Microsoft implementation here
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
template <class T, class U>
constexpr T narrow(U u) noexcept(false)
{
constexpr const bool is_different_signedness =
(std::is_signed<T>::value != std::is_signed<U>::value);
const T t = narrow_cast<T>(u);
if (static_cast<U>(t) != u || (is_different_signedness && ((t < T{}) != (u < U{}))))
{
throw narrowing_error{};
}
return t;
}
You can see the explanation of the implementation on this SO post (it's for an older version of the implementation, but nothing substantially changed, so the answer still applies).

Detect operator++ signature with std::is_detected_exact

I want to detect at compile time if a given type has the pre-increment operator with the library fundamentals TS v2 type_traits' is_detected_exact helper - however, it seems like I either misunderstood this helper or I supplied the wrong parameters, the following code does not compile:
#include <experimental/type_traits>
template<typename T>
using operator_plusplus_t = decltype(&T::operator++);
template<typename T>
using has_pre_increment = std::experimental::is_detected_exact<T&, operator_plusplus_t, T>;
struct incrementer
{
incrementer& operator++() { return *this; };
};
static_assert(has_pre_increment<incrementer>::value, "type does not have pre increment");
The error I get is this one (the static_assert fails):
<source>:14:15: error: static assertion failed: type does not have pre increment
static_assert(has_pre_increment<incrementer>::value, "type does not have pre increment");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 1
https://godbolt.org/z/-zoUd9
I was expecting this code to compile since the "incrementer" struct has a operator++ method with no arguments returning a reference to its type ...
Maybe you can point me in the right direction, thanks in advance!
You can use decltype(++std::declval<T>()) instead.
https://godbolt.org/z/h_INw-

Const correctness advice

I have a function that receives a const reference and I need to call a template library function using this reference:
std::vector<cv::Size> get_resolutions(const rs2::device& dev)
{
auto sensor = dev.first<rs2::depth_sensor>();
//more code
}
class device
{
public:
template<class T>
T first()
{
for (auto&& s : query_sensors())
{
if (auto t = s.as<T>()) return t;
}
throw rs2::error("Could not find requested sensor type!");
}
//more code
};
When I compile with gcc I get this error:
error: passing 'const rs2::device' as 'this' argument discards qualifiers [-fpermissive]
I can't change the first() function as it's part of a external library (librealsense, line 51 in here).
I can't remove the const from the function argument dev because that will result in removing const correctness in a lot of places.
I can overcome the error by removing the const from dev:
auto sensor = const_cast<rs2::device&>(dev).first<rs2::depth_sensor>();
However, this feels bad practice. Is there any more correct way of dealing with this error? I have tried unsuccessfully the following variations:
auto sensor = dev.first<const rs2::depth_sensor>();
auto sensor = const_cast<const rs2::depth_sensor>(dev.first<rs2::depth_sensor>());
but I get the same error with them.
I think there are two possible solutions to this. Either you allow get_resolutions to take dev by non-const reference (although that may require you to modify code at the call site), or you re-implement first yourself.
Option 1
Just replace
std::vector<cv::Size> get_resolutions(const rs2::device& dev)
with
std::vector<cv::Size> get_resolutions(rs2::device& dev)
This, however, would also mean that you can no longer call get_resolutions with a temporary object.
Option 2
Looking at the source of the library, however, I really can't see why first() is non-const. All it does is call query_sensors() (which is const-qualified, and also public), and process the results:1
template<class T>
T first()
{
for (auto&& s : query_sensors())
{
if (auto t = s.as<T>()) return t;
}
throw rs2::error("Could not find requested sensor type!");
}
This might be the option with the lowest impact: Just define a first() yourself, outside of the library, that replicates this functionality:
template <class T>
T custom_first(const rs2::device& dev)
{
for (auto&& s : dev.query_sensors())
if (auto t = s.as<T>())
return t;
throw rs2::error("Could not find requested sensor type!");
}
1 Time to file a bug report, maybe?

Using generic operator->* as rvalue

The compilation process of:
template <typename T> T GetMember(const T STRUCT_T::* member)
{
STRUCT_T* pStruct = GetStruct();
...
// read value
T retVal = pStruct->*member; // compiler assertion here
ReleaseStruct();
return retVal;
}
ends due to compiler assertion when used with a non-basic type T:
Tool internal error:
Internal Error: [Front end]: assertion failed at:
"....\Translator\compiler_core\src\parser\edg\lower_il.c", line 13411
Shocked by the fact that IAR compiler's "lower_il.c" has at least 13,411 lines and non of them is a proper generic operator->*(), I found it even stranger that the following function do compile with a non-basic type T:
template <typename T> void SetMember(T STRUCT_T::* member, const T& value)
{
STRUCT_T* pStruct = GetStruct();
...
// write value
pStruct->*member = value; // no compiler assertion here
ReleaseStruct();
}
I guess the result of the generic operator is OK as lvalue but not as rvalue. Unconsting the parameter didn't help.
Any ideas of cause and solution?
We can tell from edg\lower_il.c that this is the EDG frontend, not a proprietary IAR parser. EDG is well-maintained and well-respected, and you can play with a newer version at http://gcc.godbolt.org/ . (Select ICC from the compiler menu.)
The filename suggests that it's dealing with a lower-level intermediate representation, not building the initial AST. There may be a problem translating the pointer-to-member access to more primitive operations. Or the flag might be appearing on the wrong line. Internal errors aren't always precise. An SSCCE would be better.
IAR service pack 6.70.2 solved the problem.