0

As the title asks, how do I make a copy of a arbitrary type, but is different from the original type? As in, they are not implicitly convertible to each other but have the same interface.

I'll take cuda's pointers as an example to explain why this might be needed. Since host(CPU) and device(GPU) memory are separate, there is exactly 0 chance where you'd want to dereference a device pointer in host code. However, both are just plain old pointers, the compiler won't complain if you did actually do that. This is where type checking naturally fits in, these are all compile-time knowledge.

But then, to create a new pointer type, I'd have to manually type all the pointer arithmetic into a class to achieve this.

template<typename T>
class dptr
{
    //...
    dptr& operator++();
    //...
    auto& operator*();
    //...
    bool operator==(dptr) const;
    //...
    operator bool() const;
private:
    T* ptr;
};

And the boilerplate gets worse when the type to be copied is even more complicated and when noexcept is sprinkled in.

What I'm looking for is basically what Go have: strong retyping of arbitrary types

type Int int

Int is now an int, with arithmetic and all that, but is not implicitly convertible to int. (Actually, what I want is more like Go's type embedding, if the reader knows what that is.)

Passer By
  • 19,325
  • 6
  • 49
  • 96
  • `typedef`? `using`? – Some programmer dude Jun 09 '17 at 16:45
  • 2
    @Someprogrammerdude Those are aliases, they are still the same type. What I want is type safety for types with the same interface but different semantics – Passer By Jun 09 '17 at 16:46
  • 2
    Then no there is no such way except to make your own classes. To make something generic for the basic arithmetic types (the integer and floating point types) using templates is not hard (just a lot of work). – Some programmer dude Jun 09 '17 at 16:46
  • @Someprogrammerdude I'm under the same impression and is feeling depressed, so I'm asking here to see if someone has a magical hack – Passer By Jun 09 '17 at 16:47
  • @Someprogrammerdude Retyping a pointer interface might not seem like much, but try that about 20 times (each with ever so slightly different semantics) and you will be *really* ready for a good hack. – Passer By Jun 09 '17 at 16:48
  • @PasserBy: It's like 30 lines of trivial code. You already have like half of it there. – Mooing Duck Jun 09 '17 at 16:49
  • 2
    Possible duplicate of [C++ Strongly typed using and typedef](https://stackoverflow.com/questions/34287842/c-strongly-typed-using-and-typedef) – Sneftel Jun 09 '17 at 16:49
  • @Sneftel No, I know how to retype all the methods and be happy about that. I'm asking how to do this for arbitrary types without having stupid amounts of boilerplate. – Passer By Jun 09 '17 at 16:52
  • @MooingDuck The code is an example. Try doing that to any complicated type for more than a few times, you'd be surprised how quickly it gets tedious. – Passer By Jun 09 '17 at 16:57
  • @PasserBy: Also, I just realized you probably also want to do this for classes, not just primitives. Indeed, that would get tedious. – Mooing Duck Jun 09 '17 at 17:06

1 Answers1

3

I can think of couple of ways to do that. Both use strongly typed typedefs.

The first method assumes that you have the ability to define the main class, which can be a class template instead of a class.

template <typename Tag>
struct foo
{
   void bar() {}
};

using Type1 = foo<int>;
using Type2 = foo<float>;

int main()
{
   Type1 t1;
   Type2 t2;

   t1.bar(); // OK
   t2.bar(); // OK
   t2 = t1;  // Incorrect.
}

The second method is similar but it uses existing classes. It assumes that you don't have the ability to modify the main class.

// Existing class.
struct foo
{
   void bar() {}
};

// Bar can be used to create strongly typed typedefs.
template <typename Tag, typename Base>
struct Bar : public Base
{
};

using Type1 = Bar<int, foo>;
using Type2 = Bar<float, foo>;

int main()
{
   Type1 t1;
   Type2 t2;

   t1.bar(); // OK
   t2.bar(); // OK
   t2 = t1;  // Incorrect.
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • @PasserBy, That's under your control. You can create empty `struct`s to be used as tags. – R Sahu Jun 09 '17 at 17:11
  • Wait, I just remembered why I didn't like this solution before: what do I do when you have stuff like `operator==(const foo&)` in `foo`. [`t1 == t2` compiles](http://coliru.stacked-crooked.com/a/cb21bfe838720042) – Passer By Jun 09 '17 at 17:21
  • @PasserBy, your can disallow that by adding `constexpr bool operator==(const Bar&) {return true;}` to `Bar`. – R Sahu Jun 09 '17 at 17:53
  • That's what I'm asking to avoid :) Any method that has a parameter that is itself will have this problem – Passer By Jun 10 '17 at 06:12
  • @PasserBy, that's not much of a burden, is it? – R Sahu Jun 10 '17 at 06:15