C++ template function to compare any unsigned and signed integers - c++

I would like to implement a template function that compares two variables of two types (T1 and T2). These types are two random unsigned or signed integer types.
To be able to compare them correctly I need to cast both of them to a 'bigger' integer type (T3). Promotion rules for signed/unsigned comparison unfortunately always promote to the unsigned type.
So how can I find a type T3 in C++11/C++14/C++17 that covers two integer types T1 and T2, no matter which size and signedness they have?
If this isn't possible, is there an other solution to build a template based comparison function that works reliably with any integer combination?

You can split the comparison up into parts. First check if one number is negative, and the other positive. If that's the case you know what order they go in. If neither is negative (or both are), just do a normal comparison.
This can be built in a template function that'll only check for negative of signed types.

I am not sure I understand your question. Do you mean something like this:
#include <cstdint>
#include <type_traits>
template < typename P, typename Q >
auto
compare( P p, Q q ) {
using T = typename std::common_type< P, Q >::type;
T promoted_p{p};
T promoted_q{q};
if ( promoted_p < promoted_q ) {
return -1;
}
else if ( promoted_p > promoted_q ) {
return 1;
}
else {
return 0;
}
}
It will work when safe to do so, and you can add your specializations if the language is not doing what you want.

Related

Smallest int value that is smaller than the smallest representable int?

std::numeric_limits<T>::infinity() returns 0 if T is an integral type. Is there a replacement for this function that I can use? I have a series of int variables (they could be int8, uint8, int32, uint32, int64, uint64) that could take on values anywhere between their minimum (inclusive) and maximum (inclusive) values.
I need a value that is smaller than all of these variables, and this value will be used to comparison purposes (I would be comparing whether this value is smaller than my series of int variables).
I know I could something like -std::numeric_limits<double>::infinity(), but if I were to compare my int variables with this value, the comparison would be done in FP precision, and I'm concerned about the performance implications of that. I would, ideally, like all the comparisons to be done with integers.
I think the smallest int value that we can have is std::numeric_limits<int64_t>::minimum(), so I think my question is essentially asking if we can get an integer value smaller than this?
You can define your own minus infinity constant that compares less than any other value.
This is less tedious with C++20's three way comparator (aka spaceship), but feasible in previous versions:
#include <compare>
struct minus_infinity_t {} constexpr minus_infinity;
auto constexpr operator<=> (minus_infinity_t, minus_infinity_t)
{ return std::weak_ordering::equivalent; }
template<class T> auto constexpr operator<=> (T, minus_infinity_t)
{ return std::weak_ordering::greater; }
template<class T> auto constexpr operator<=> (minus_infinity_t, T)
{ return std::weak_ordering::less; }
int main()
{
constexpr int n = 0;
constexpr long long k = 1;
constexpr double l = 2;
static_assert(n > minus_infinity);
static_assert(k > minus_infinity);
static_assert(l > minus_infinity);
static_assert(minus_infinity <= n);
static_assert(minus_infinity <= k);
static_assert(minus_infinity <= l);
}
Live demo
Improvement ideas:
Restrict what T can be with concepts
Define a positive infinity
Define a negative and positive zero

How to explicitly cast argument to match an expected function parameter?

I am trying to write a generic function to compute an average over a certain range.
template <typename Range, typename Ret, typename Func>
Ret average(Range range, Ret zero, Func extract) {
Ret sum = zero;
int numElements = 0;
for (const auto& elem : range) {
sum += extract(elem);
++numElements;
}
if (numElements > 0)
sum /= numElements;
return sum;
}
The problam I am having is with the usage of the /= operator, but to better clarify the arguments of this function, let me clarify them:
Range range is any object that defines a range through begin() and end() member funcitons. I may need to add const& to avoid unnecessary copying.
Ret zero defines the neutral element of the addition used when computing the average. It could be just a scalar, but will work with vectors or matrices too for example.
Func extract is a function (usually given as a lambda function) that converts the elements of the range into Ret values that I average over. In practice I use it as a getter of a specific field in big objects that I iterate over.
I could probably define it as std::function<Ret(decltype(*range.begin()))> or something similar, if C++ didn't have problems deducting types this way.
I assume that Ret provides some /= operator that the above function can work with, but I do not want to require it to take an int specifically.
In my use case, for example, Ret works with float-s and this gives me an annoying warning:
warning: 'argument': conversion from 'int' to 'float', possible loss of data
So, what are my options to make the above function clean and work with any suitable operator/=?
I tried, for example, to deduct the type of the right argument of the operator and explicitly cast to it:
template <typename Range, typename Ret, typename Func>
Ret average(Range range, Ret zero, Func extract) {
Ret sum = zero;
int numElements = 0;
for (const auto& elem : range) {
sum += extract(elem);
++numElements;
}
using F = std::remove_pointer<decltype(&Ret::operator/=)>;
if (numElements > 0)
sum /= static_cast<typename boost::function_traits<F>::arg1_type>(numElements);
return sum;
}
But I get a lot of compile errors, suggesting that I don't know what I am doing. Starts with:
error: 'boost::detail::function_traits_helper<std::remove_pointer<SpecificTypeUsedAsRet &(__cdecl SpecificTypeUsedAsRet::* )(float)> *>': base class undefined
That's probably because boost::function_traits does not work with member functions, just regular ones?
I am also concerned that this solution may not work when:
The operator/= is not given as a member function, but as a regular function with two arguments.
The operator/= is overloaded with respect to its right operand. An int may match only one of the overloads - so there is no ambiguity, but decltype won't know which overload to take.
I would prefer not to use boost but stick to the powers provided by newest C++ standards
You could simply declare Ret numElements = 0; instead of making it an int. If it has /= operator, it probably has an ++ operator; or you could use num_elements += 1 instead.

General iterable type with specific element type

I'm trying to write a function for enumerating through a number of a specific base, where the number is stored in some kind of list. Here is an example, taking a std::vector
void next_value(std::vector<unsigned int> &num, unsigned int base) {
unsigned int carry = 1;
for (unsigned int &n: num) {
n += carry;
if (n >= base) {
carry = 1;
n = 0;
} else {
carry = 0;
}
}
}
The num vector doesn't necessarily need to be a vector, it can be an array, or actually any type that has a std::begin() and std::end() defined for it. Is there a way to express that num can be anything with begin() and end(), but that it must have unsigned int type for its elements?
If you really want to check this, try:
template <class Sequence>
void next_value(Sequence &num, unsigned int base) {
static_assert(boost::is_same<Sequence::value_type, unsigned>::value, "foo");
// ...
If you're not using C++11 yet, use BOOST_STATIC_ASSERT instead.
If you need to support plain C-style arrays, a bit more work is needed.
On the other hand, #IgorTandetnik correctly points out that you probably do not need to explicitly check at all. The compiler will give you an (ugly) error if you pass a type which is truly unusable.
Writing a generic function with a static_assert is a good idea, because you can give the user a helpful error message rather than "foo".
However there is another approach using C++11:
template <typename Container, typename ValueType>
typename std::enable_if<std::is_same<Container::value_type, ValueType>::value, void>::type
next_value(Container& num, ValueType base)
{
// ...
}
This is a rather cryptic approach if you've never seen this before. This uses "Substitution failure is not an error" (SFINAE for short). If the ValueType doesn't match the Container::value_type, this template does not form a valid function definition and is therefore ignored. The compiler behaves as if there is not such function. I.e., the user can't use the function with an invalid combination of Container and ValueType.
Note that I do recommend using the static_assert! If you put a reasonable error message there, the user will thank you a thousand times.
I would not in your case.
Change carry to a book, use ++ instead of +=, make base a type T, and n an auto&.
Finally, return carry.
Your code now ducktypes exactly the requirements.
If you want diagnostics, static assert that the operations make sense with custom error messages.
This let's your code handle unsigned ints, polynomials, bigints, whatever.

How to compare vectors approximately in Eigen?

Is there a function in Eigen to compare vectors (matrices) using both relative and absolute tolerance aka numpy.allclose? Standard isApprox fails if one of the vectors is very close to zero.
There is no built-in function implementing numpy.allclose, but you easily write one yourself if that's really what you need. However, I'd rather suggest the use of isMuchSmallerThan with reference value:
(a-b).isMuchSmallerThan(ref)
where ref is a representative non zero for your problem.
EDIT: for reference here is a possible implementation of allclose:
template<typename DerivedA, typename DerivedB>
bool allclose(const Eigen::DenseBase<DerivedA>& a,
const Eigen::DenseBase<DerivedB>& b,
const typename DerivedA::RealScalar& rtol
= Eigen::NumTraits<typename DerivedA::RealScalar>::dummy_precision(),
const typename DerivedA::RealScalar& atol
= Eigen::NumTraits<typename DerivedA::RealScalar>::epsilon())
{
return ((a.derived() - b.derived()).array().abs()
<= (atol + rtol * b.derived().array().abs())).all();
}
There is also isApprox function which was not working for me.
I am just using ( expect - res).norm() < some small number.

changing/adding behavior of stl bitset<>::reference::operator=(int)

I need a bitset with a slightly diffrent behavior when asigning variables with integer type to a specific bit. The bit should be set to zero if the assigned integer is smaller then one, and to one elsewise.
As a simple solution I copied the STL bitset, replaced the classname with altbitset, adjusted namespaces and include guard and added following function under reference& operator=(bool __x) in the nested reference class:
template <typename T>
reference& operator=(T i) {
if (i<1) return operator=(false);
return operator=(true);
}
It works as expected.
Question is if there is a better way doing this.
You shouldn't copy a library just to add a new function. Not only that, the new function is wildly unintuitive and could possibly be the source of errors for even just reading the code, let alone writing it.
Before:
bv[n] = -1; // I know a Boolean conversion on -1 will take place
assert(bv[n]); // of course, since -1 as a Boolean is true
After:
bv[n] = -1; // I guess an integer < 1 means false?
assert(bv[n]); // Who changed my bitvector semantics?!
Just write it out so it makes sense in your domain:
bv[n] = (i < 1);
Remember: simplest doesn't always mean fewest characters, it means clearest to read.
If you do want to extend the functionality of existing types, you should do so with free functions:
template <typename BitSet, typename Integer>
auto assign_bit_integer(BitSet& bits, const std::size_t bit, const Integer integer) ->
typename std::enable_if<std::is_integral<Integer>::value,
typename BitSet::reference>::type
{
return bits[bit] = (integer < 1);
}
Giving:
std::bitset<8> bits;
assign_bit_integer(bits, 0, 5);
// ERROR: assign_bit_integer(bits, 0, 5.5);
But for such a small function with no clear "obvious" name that describes what it does concisely(assign_bit_true_if_less_than_one_otherwise_false is verbose, to say the least), just write out the code; it says the same thing anyway.