themole
themole

Reputation: 383

Is it possible in C++ to conditionally compile code if types are (not) equal?

Currently I have a problem with code portability from 64 (back) to 32 bit. The problem is, that a method of a class is overloaded for 64 bit platforms that conflicts with another overload on 32 bit platforms.

The two methods look like this:

void myfunc(unsigned o);
void myfunc(size_t o);

On 32 bit architectures, they look identical to the compiler and throw some error.

So, the question is, is it possible to do something like this:

void myfunc(unsigned o);
#if typeid(unsigned) != typeid(size_t)
void myfunc(size_t o);
#endif

My current solution looks like this:

void myfunc(unsigned o);
#if __WORDSIZE == 64
void myfunc(size_t o);
#endif

But there is some bad feeling left, that WORDSIZE is not the best fit, because it does not necessarily mean, that the types are not the same.

OK, here is the problematic place line 705 and 706, that produce an error when compiling on 32-bit ARM.

ceph/src/include/buffer.h

Upvotes: 1

Views: 1093

Answers (3)

wilx
wilx

Reputation: 18228

You can use std::enable_if for that:

#include <type_traits>

struct S {
      void advance(unsigned o);
      std::enable_if<!std::is_same<unsigned, std::size_t>::value>::type
      advance(std::size_t o) { advance(static_cast<unsigned>(o)); }
};

Though, as others have already pointed out, I would ditch the unsigned variant and keep only the std::size_t variant.

Upvotes: 0

user10605163
user10605163

Reputation:

If the underlying C standard library supports the "Floating-point extensions part 1" (ISO/IEC TS 18661-1:2014), then you have preprocessor macros available that can be used to identify the sizes of types:

#include<climits>
#include<cstdint>

void myfunc(unsigned o);
#if UINT_WIDTH != SIZE_WIDTH
void myfunc(size_t o);
#endif

This is supported e.g. by glibc. Beware that the test always fails if the macros are not defined, i.e. if the specification is not implemented, so you should probably check for that as well, i.e.

#if UINT_WIDTH != SIZE_WIDTH || !defined(UINT_WIDTH) || !defined(SIZE_WIDTH)

Without such implementation-defined macros, the preprocessor cannot be used to achieve what you want, as it doesn't actually know about C or C++ types.

Any solution at the C++ compilation level will require you to at least somewhat modify the function declarations.

I do not consider this solution particularly clean, but your current solution isn't either. Really, if as I suspect, the goal is to avoid certain implicit conversions, the method should be a template with a static_assert limiting the type as appropriate.


Edit:

The code above works as is with a current glibc and gcc, but I am not sure whether technically this is correct behavior. This is a technical specification extending C11, not C++. I don't know how or whether C++ incorporates these or if they would be considered implementation-defined extensions.

Also according to the specification the macros should only be defined if you

#define __STDC_WANT_IEC_60559_BFP_EXT__

before the first #include<stdint.h> or #include<limits.h>. When compiling in C mode GCC with glibc does actually require this.

Whether the specification is implemented can be checked by comparing the macro __STDC_IEC_60559_BFP__ against 201ymmL. However GCC with glibc does not seem to set this macro and the documentation notes that support of the specification is only partial.

Probably you should at least make sure that UINT_WIDTH and SIZE_WIDTH are set before trusting the comparison made above. If they are not, e.g. because the specification is not supported is will evaluate always to 0 != 0, i.e. false.

Upvotes: 3

Victor Padureanu
Victor Padureanu

Reputation: 614

This might be an option using templates:

#include <iostream>

class A {
public:
    template<typename T>
    std::enable_if_t<std::is_same<T, int>::value || 
    std::is_same<T, unsigned>::value ||
    std::is_same<T, std::size_t>::value >
    advance(T o) {
        std::cout << "s" << std::endl;
        A::advance<unsigned>(static_cast<unsigned>(o));
    }
};

template<>
    void A::advance(int o) = delete;

template<>
    void A::advance(unsigned o) {
        std::cout << "u" << std::endl;
    }

int main()
{
  A a;

  unsigned x;
  std::size_t y;
  int z;
  char p;

  a.advance(x);
  a.advance(y);
  //a.advance(z);
  //a.advance(p);

  return 0;
}

Upvotes: 1

Related Questions