8

I have some headache inducing problems here.

Basically I'm trying to make a library compatible with different Arduino systems (not an Arduino question).

I have a situation where types no longer match, as in an int is no longer equivalent to its identical fixed width type. In the limited environment provided (no stdlib and such) I have written my own type trait classes for the features I need.

Everything works fine using GCC 4.8.1 (avr) & Extensa-1x106-GCC (ESP8266), but not in GCC 4.8.3 (SAM,SAMD cores).

Basically I have knuckled down my code to show the problem in this very basic code (int is confirmed to have 4 bytes on the failing 32-bit platform compilers):

template < typename T, typename U > struct is_same{ enum { value = false }; };
template < typename T > struct is_same< T, T >    { enum { value = true }; };

void setup() {
  static_assert( is_same<int,int32_t>::value, "Not integer");
}

void loop(){}

You can view a 'normal' C++ implementation here (above is a basic implementation for use within the Arduino IDE): http://cpp.sh/377e

By the way, the static assert does not fire in the cpp.sh compiler either.

Is 4.8.1 incorrect, meaning int and int32_t should be considered different types. Or is 4.8.3 incorrect and they should be equivalent if equally sized defined by the implementation.

I was using the code below to detect any type of integer, which was where I found my error originally.

template< typename T >
    struct is_integer{
        enum{
            V8    = is_same< T, uint8_t >::value  || is_same< T, int8_t >::value,
            V16   = is_same< T, uint16_t >::value || is_same< T, int16_t >::value,
            V32   = is_same< T, uint32_t >::value || is_same< T, int32_t >::value,
            V64   = is_same< T, uint64_t >::value || is_same< T, int64_t >::value,
            value = V8 || V16 || V32 || V64
        };
};

I of course can change it to check for char, int, long, etc.. but it will still require checking for all fixed width variations and most likely the int_fastX_t and int_leastX_t types, which seems like a super redundant method to ensure maximum usability.

Any ideas?

Cheers, I appreciate any input!

Chris A
  • 1,475
  • 3
  • 18
  • 22
  • 1
    Is the toolchain in question available somewhere? – melak47 Sep 28 '15 at 11:57
  • can you use [boost](http://www.boost.org/doc/libs/1_59_0/libs/integer/doc/html/boost_integer/traits.html) in your embedded environment? – m.s. Sep 28 '15 at 11:59
  • @melak47 Yeah, you can download the [Arduino IDE](https://www.arduino.cc/en/Main/Software) and from the menu Tools->Board->Board manager select and install either the SAM, or SAMD core for one of the failing chains (it will do the rest), then select either the Zero(SAMD) or Due(SAM) from the Tools->Board list. (It is a pain, but designed for beginners). The AVR 4.8.1 core is provided by default. – Chris A Sep 28 '15 at 12:02
  • Have you checked the releveant stdint.h/cstdint header file ? It might very well typedef int32_t to be `long`, (or another 32 bit integer if that exists ) – nos Sep 28 '15 at 12:03
  • @m.s. Boost is most probably possible, however I would like to know what the core of the problem is. – Chris A Sep 28 '15 at 12:03
  • @nos my is_integer compares the imput to all variations of fixed width types, none match! – Chris A Sep 28 '15 at 12:05
  • 1
    Btw it might not be the versions of GCC that differ (4.8.1 vs 4.8.3), rather it might be the architectures (avr vs SAM). I haven't checked, but it would be unusual and inconvenient for GCC to make this kind of change in a minor release. Whereas different architectures having their integer types set up differently is completely normal. – Steve Jessop Sep 28 '15 at 12:18
  • @ChrisA Still, wouldn't it be interesting to see what int32_t actually is, in your environment ? – nos Sep 28 '15 at 12:48
  • 1
    According to the compiler, int must be in there after all: https://gist.github.com/melak47/45ac3cc2be3f61fcd994 – melak47 Sep 28 '15 at 12:54
  • 1
    And according to [this](https://gist.github.com/melak47/e0bff92a5b126962b5d5), int seems to be `int_fast8_t`, `int_fast16_t` and `int_fast32_t` on this particular platform :) – melak47 Sep 28 '15 at 13:00
  • You only need to define your `is_integral` for the fundamental types. All the intXX_t types are just typedefs. – Vaughn Cato Sep 28 '15 at 13:56
  • I'm looking through the amazing responses provided, definitely a learning experience, I'll have to look at everything in the morning when I have a clear head. @melak47 thank you so much, highly appreciated. @SteveJessop, seems on the money. @VaughnCato I'll try this and see how it copes with the different compilers (would be the simplest), I was planning to simply cut and paste to cover everything, but your solution may suffice (for the TC's I need to be compatible with). Although, what I understand there is the possibility of a compiler not having `int` equal to any fixed/fast/least type... – Chris A Sep 28 '15 at 14:19
  • You are right, there is no requirement that there be a stdint typedef that is equivalent to `int`, but how is this a problem? The typedef names are not distinct types, they are just aliases for one of the built-in types. If you have all the built-in types covered, then you should be fine. – Vaughn Cato Sep 28 '15 at 15:03
  • @VaughnCato The problem is that they can be aliases to compiler-provided types *distinct* from all fundamental types. Nothing prevents a compiler from including a type `__int32` distinct from `int`, and typedefing `int32_t` to `__int32`. – Angew is no longer proud of SO Sep 28 '15 at 19:51

2 Answers2

5

This is governed by the C standard; the C++ one just inherits the behaviour by explicit reference.

What the C standard says is:

  • If int32_t is defined, it refers to a signed 32-bit 2's complement integer.

  • If the implementation provides a signed 32-bit 2's complement integer type, it must provide the typedef int32_t which will refer to it.

Nowhere does it say that this 32-bit 2's complement signed integer type must be int. Technically, even if int is a 32-bit 2's complement integer type, it is perfectly possible for the implementation to also provide a distinct 32-bit 2's complement signed integer type and define int32_t to refer to that other type.

I am afraid the only fully generic solution would be to list all the fundamental types, fixed-width types, minimum-width types, and fast minimum-width types.

For something less daunting, it should be possible to inspect the documentation of the toolchains you wish to support to find what types they provide and how they name them. If this set of "toolchains you wish to support" is unbounded, I don't think there's an easier way out.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    You can make this a bit less insane by making a traits template that produces fast, fixed-width, min-width, fast min-width for a given size and signedness. That at least breaks the lists down into managable chunks. Toss in type lists of all the fundamental types (and signedness), plus size_t and other similar integral types of undefined providence, and then some metaprogramming to operate on all of these uniformly... – Yakk - Adam Nevraumont Sep 28 '15 at 15:27
  • Listing all the types definitely would cover it all, however in all compilers tested (4 different architectures) replacing the fixed width integers with the fundamental types has proved to be fine for my situation. It might need updating in the future, but most uses will involve an integer literal. Accepted answer. – Chris A Dec 30 '15 at 14:33
1

From the C11 standard 7.20.1.1(1)

The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two’s complement representation. Thus, int8_t denotes such a signed integer type with a width of exactly 8 bits.

So int32_t is a signed integer that is exactly 32 bits wide.

An int though is defined as sizeof(int) being greater than or equal to a char(C++14 3.9.1(2)) and that an signed int must be able to represent [-32767, 32767](C11 5.2.4.2.1). That range is actually 16 bits.

So an int may never be equivalent to a intN_t as an intN_t can be a implementation defined type separate from a standard type.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • The width of the implemented `int` doesn't matter, I compare it to all fixed width variants. None match. – Chris A Sep 28 '15 at 12:06