How to dispatch on the size of Int?


(Martin R) #1

I wonder what the best way would be to call a specialized function dependent on the size of `Int`. Let's say that I have two implementations

    func foo_impl(value: Int32) { /* ... */ }
    func foo_impl(value: Int64) { /* ... */ }

and I want

    func foo(value: Int)

to call the "right one" of them, according to the architecture (32-bit or 64-bit).

    func foo(value: Int) { foo_impl(value: value) }
   
does not compile. (I assume that is because `Int` is not a type alias to `Int32` or `Int64` but an independent type.)

This works:

    func foo1(value: Int) {
        if MemoryLayout<Int>.size == 4 {
            foo_impl(value: Int32(value))
        } else {
            foo_impl(value: Int64(value))
        }
    }

or

    func foo2(value: Int) {
        switch MemoryLayout<Int>.size {
        case 4: foo_impl(value: Int32(value))
        case 8: foo_impl(value: Int64(value))
        default:
            abort()
        }
    }

But a typo in the constants would go unnoticed and just call the wrong function, or cause a runtime error instead of a compile-time error. And perhaps `Int` can be an 128-bit integer in the future? The compiler would not warn that the code needs to be updated.

This seems to be more promising:

    func foo3(value: Int) {
        switch (__WORDSIZE) {
        case 32: foo_impl(value: Int32(value)) // Warning: Will never be executed
        case 64: foo_impl(value: Int64(value))
        }
    }
    
Apparently the compiler "knows" which case will be executed, `foo3` does not compile if there is no case matching actual integer size. But there is always an annoying warning for the unused case. And I am not sure if it is guaranteed that __WORDSIZE is the number of bits in an `Int`.

So my question is: What would be the best way to dispatch dependent on the size of `Int`, such that

- The compiler checks the correctness.
- The compiler optimizes the code so that no runtime check is done.
- No warnings are produced.

If `Int` had a `native` property like `CGFloat` then I could simply call

    func foo(value: Int) { foo_impl(value: value.native) }

but I could not find such a property. (Would that be a reasonable thing to ask on swift-evolution?)
    
Background: I am asking this just out of curiosity, but the question came up when looking at the `hash_combine` function in the Boost library:

  http://www.boost.org/doc/libs/1_62_0/boost/functional/hash/hash.hpp
  
with quite different implementations

    inline void hash_combine_impl(boost::uint32_t& h1, boost::uint32_t k1)
    inline void hash_combine_impl(boost::uint64_t& h, boost::uint64_t k)
    
and I wondered how this would be done in Swift.

Regards,
Martin


(Hooman Mehr) #2

func foo_impl(value: Int32) { /* ... */ }
func foo_impl(value: Int64) { /* ... */ }

func foo(value: Int) {
    #if arch(i386) || arch(arm)
        foo_impl(value: Int32(value))
    #else
        foo_impl(value: Int64(value))
    #endif
}

···

On Nov 23, 2016, at 1:31 PM, Martin R via swift-users <swift-users@swift.org> wrote:

I wonder what the best way would be to call a specialized function dependent on the size of `Int`. Let's say that I have two implementations

   func foo_impl(value: Int32) { /* ... */ }
   func foo_impl(value: Int64) { /* ... */ }

and I want

   func foo(value: Int)

to call the "right one" of them, according to the architecture (32-bit or 64-bit).

   func foo(value: Int) { foo_impl(value: value) }

does not compile. (I assume that is because `Int` is not a type alias to `Int32` or `Int64` but an independent type.)

This works:

   func foo1(value: Int) {
       if MemoryLayout<Int>.size == 4 {
           foo_impl(value: Int32(value))
       } else {
           foo_impl(value: Int64(value))
       }
   }

or

   func foo2(value: Int) {
       switch MemoryLayout<Int>.size {
       case 4: foo_impl(value: Int32(value))
       case 8: foo_impl(value: Int64(value))
       default:
           abort()
       }
   }

But a typo in the constants would go unnoticed and just call the wrong function, or cause a runtime error instead of a compile-time error. And perhaps `Int` can be an 128-bit integer in the future? The compiler would not warn that the code needs to be updated.

This seems to be more promising:

   func foo3(value: Int) {
       switch (__WORDSIZE) {
       case 32: foo_impl(value: Int32(value)) // Warning: Will never be executed
       case 64: foo_impl(value: Int64(value))
       }
   }

Apparently the compiler "knows" which case will be executed, `foo3` does not compile if there is no case matching actual integer size. But there is always an annoying warning for the unused case. And I am not sure if it is guaranteed that __WORDSIZE is the number of bits in an `Int`.

So my question is: What would be the best way to dispatch dependent on the size of `Int`, such that

- The compiler checks the correctness.
- The compiler optimizes the code so that no runtime check is done.
- No warnings are produced.

If `Int` had a `native` property like `CGFloat` then I could simply call

   func foo(value: Int) { foo_impl(value: value.native) }

but I could not find such a property. (Would that be a reasonable thing to ask on swift-evolution?)

Background: I am asking this just out of curiosity, but the question came up when looking at the `hash_combine` function in the Boost library:

http://www.boost.org/doc/libs/1_62_0/boost/functional/hash/hash.hpp

with quite different implementations

   inline void hash_combine_impl(boost::uint32_t& h1, boost::uint32_t k1)
   inline void hash_combine_impl(boost::uint64_t& h, boost::uint64_t k)

and I wondered how this would be done in Swift.

Regards,
Martin

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Martin R) #3

Yes, I had forgotten about that, thank you! That would satisfy all criteria. And with

    func foo(value: Int) {
    #if arch(i386) || arch(arm)
        foo_impl(value: Int32(value))
    #elseif arch(x86_64) || arch(arm64)
        foo_impl(value: Int64(value))
    #endif
    }

it should be „future-safe“, i.e. fail to compile on a new platform. (I would still prefer a solution which does not require me to know about all possible processor architectures.)

···

Am 23.11.2016 um 23:06 schrieb Hooman Mehr <hooman@mac.com>:

func foo_impl(value: Int32) { /* ... */ }
func foo_impl(value: Int64) { /* ... */ }

func foo(value: Int) {
    #if arch(i386) || arch(arm)
        foo_impl(value: Int32(value))
    #else
        foo_impl(value: Int64(value))
    #endif
}

On Nov 23, 2016, at 1:31 PM, Martin R via swift-users <swift-users@swift.org> wrote:

I wonder what the best way would be to call a specialized function dependent on the size of `Int`. Let's say that I have two implementations

   func foo_impl(value: Int32) { /* ... */ }
   func foo_impl(value: Int64) { /* ... */ }

and I want

   func foo(value: Int)

to call the "right one" of them, according to the architecture (32-bit or 64-bit).

   func foo(value: Int) { foo_impl(value: value) }

does not compile. (I assume that is because `Int` is not a type alias to `Int32` or `Int64` but an independent type.)

This works:

   func foo1(value: Int) {
       if MemoryLayout<Int>.size == 4 {
           foo_impl(value: Int32(value))
       } else {
           foo_impl(value: Int64(value))
       }
   }

or

   func foo2(value: Int) {
       switch MemoryLayout<Int>.size {
       case 4: foo_impl(value: Int32(value))
       case 8: foo_impl(value: Int64(value))
       default:
           abort()
       }
   }

But a typo in the constants would go unnoticed and just call the wrong function, or cause a runtime error instead of a compile-time error. And perhaps `Int` can be an 128-bit integer in the future? The compiler would not warn that the code needs to be updated.

This seems to be more promising:

   func foo3(value: Int) {
       switch (__WORDSIZE) {
       case 32: foo_impl(value: Int32(value)) // Warning: Will never be executed
       case 64: foo_impl(value: Int64(value))
       }
   }

Apparently the compiler "knows" which case will be executed, `foo3` does not compile if there is no case matching actual integer size. But there is always an annoying warning for the unused case. And I am not sure if it is guaranteed that __WORDSIZE is the number of bits in an `Int`.

So my question is: What would be the best way to dispatch dependent on the size of `Int`, such that

- The compiler checks the correctness.
- The compiler optimizes the code so that no runtime check is done.
- No warnings are produced.

If `Int` had a `native` property like `CGFloat` then I could simply call

   func foo(value: Int) { foo_impl(value: value.native) }

but I could not find such a property. (Would that be a reasonable thing to ask on swift-evolution?)

Background: I am asking this just out of curiosity, but the question came up when looking at the `hash_combine` function in the Boost library:

http://www.boost.org/doc/libs/1_62_0/boost/functional/hash/hash.hpp

with quite different implementations

   inline void hash_combine_impl(boost::uint32_t& h1, boost::uint32_t k1)
   inline void hash_combine_impl(boost::uint64_t& h, boost::uint64_t k)

and I wondered how this would be done in Swift.

Regards,
Martin

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Hooman Mehr) #4

I agree, there should be a platform condition that just indicates native word size, such as arch(32bit) and arch(64bit).

···

On Nov 23, 2016, at 2:18 PM, Martin R <martinr448@gmail.com> wrote:

Yes, I had forgotten about that, thank you! That would satisfy all criteria. And with

   func foo(value: Int) {
   #if arch(i386) || arch(arm)
       foo_impl(value: Int32(value))
   #elseif arch(x86_64) || arch(arm64)
       foo_impl(value: Int64(value))
   #endif
   }

it should be „future-safe“, i.e. fail to compile on a new platform. (I would still prefer a solution which does not require me to know about all possible processor architectures.)

Am 23.11.2016 um 23:06 schrieb Hooman Mehr <hooman@mac.com>:

func foo_impl(value: Int32) { /* ... */ }
func foo_impl(value: Int64) { /* ... */ }

func foo(value: Int) {
   #if arch(i386) || arch(arm)
       foo_impl(value: Int32(value))
   #else
       foo_impl(value: Int64(value))
   #endif
}

On Nov 23, 2016, at 1:31 PM, Martin R via swift-users <swift-users@swift.org> wrote:

I wonder what the best way would be to call a specialized function dependent on the size of `Int`. Let's say that I have two implementations

  func foo_impl(value: Int32) { /* ... */ }
  func foo_impl(value: Int64) { /* ... */ }

and I want

  func foo(value: Int)

to call the "right one" of them, according to the architecture (32-bit or 64-bit).

  func foo(value: Int) { foo_impl(value: value) }

does not compile. (I assume that is because `Int` is not a type alias to `Int32` or `Int64` but an independent type.)

This works:

  func foo1(value: Int) {
      if MemoryLayout<Int>.size == 4 {
          foo_impl(value: Int32(value))
      } else {
          foo_impl(value: Int64(value))
      }
  }

or

  func foo2(value: Int) {
      switch MemoryLayout<Int>.size {
      case 4: foo_impl(value: Int32(value))
      case 8: foo_impl(value: Int64(value))
      default:
          abort()
      }
  }

But a typo in the constants would go unnoticed and just call the wrong function, or cause a runtime error instead of a compile-time error. And perhaps `Int` can be an 128-bit integer in the future? The compiler would not warn that the code needs to be updated.

This seems to be more promising:

  func foo3(value: Int) {
      switch (__WORDSIZE) {
      case 32: foo_impl(value: Int32(value)) // Warning: Will never be executed
      case 64: foo_impl(value: Int64(value))
      }
  }

Apparently the compiler "knows" which case will be executed, `foo3` does not compile if there is no case matching actual integer size. But there is always an annoying warning for the unused case. And I am not sure if it is guaranteed that __WORDSIZE is the number of bits in an `Int`.

So my question is: What would be the best way to dispatch dependent on the size of `Int`, such that

- The compiler checks the correctness.
- The compiler optimizes the code so that no runtime check is done.
- No warnings are produced.

If `Int` had a `native` property like `CGFloat` then I could simply call

  func foo(value: Int) { foo_impl(value: value.native) }

but I could not find such a property. (Would that be a reasonable thing to ask on swift-evolution?)

Background: I am asking this just out of curiosity, but the question came up when looking at the `hash_combine` function in the Boost library:

http://www.boost.org/doc/libs/1_62_0/boost/functional/hash/hash.hpp

with quite different implementations

  inline void hash_combine_impl(boost::uint32_t& h1, boost::uint32_t k1)
  inline void hash_combine_impl(boost::uint64_t& h, boost::uint64_t k)

and I wondered how this would be done in Swift.

Regards,
Martin

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users