Author Topic: Using versus Typedef  (Read 3401 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Using versus Typedef
« on: August 30, 2018, 01:15:40 PM »
You can create type aliases using either using (newer C++ syntax) or typedef (older C syntax). The syntax for using seems a little more natural and easier to read. Additionally, using can be used with templates, whereas it's an error to try and create a template with typedef.

Here's an example contrasting the two (using awful pointer-to-member-function syntax):
Code: cpp [Select]

class ClassName;

/* Fixed class type */
typedef void (ClassName::*MemberFunctionPtr1)();
using MemberFunctionPtr2 = void (ClassName::*)();

/* Templated class type */
template<typename T> typedef void (T::*MemberFunctionPtr3)(); /* Error */
template<typename T> using MemberFunctionPtr4 = void (T::*)();


Offline lordpalandus

  • Banned
  • Hero Member
  • *****
  • Posts: 825
Re: Using versus Typedef
« Reply #1 on: August 30, 2018, 01:48:42 PM »
Is the keyword "using" a preprocessor, like a "typedef" is?
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Using versus Typedef
« Reply #2 on: August 30, 2018, 09:10:42 PM »
typedef isn't part of the preprocessor, it's a specifier. Preprocessor directives are prefixed with '#'... e.g., #include, #if/#endif, #define/#undef, #pragma, etc.



Like I said on IRC, in your examples I prefer the typedef'd version. It does have the limit that it can't be templatized but I don't really see a use case that would really make using any more attractive to me than typedef.

But that's just me. Maybe it's just because that's what I'm used to seeing.
« Last Edit: August 31, 2018, 07:10:37 PM by leeor_net »

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: Using versus Typedef
« Reply #3 on: August 31, 2018, 12:22:01 AM »
It does have the limit that it can't be templatized but I don't really see a use case that would really make using any more attractive to me than typedef.
type_traits in C++14 adds templated using aliases, which make using it much less verbose - otherwise you have to specify typename and ::type all over the place, when type_traits is already disgustingly verbose to begin with.

Also, if there's some templated container class or whatever that also takes other template args like an allocator, or initial size, etc., you can make a template using alias that takes the container item type and fills in the other args for you, like:
Code: [Select]
template <typename T, size_t InitialSize, typename Allocator>
class MySmallVector {
  ...
  std::aligned_storage_t<sizeof(T), alignof(T)> initialBuffer[InitialSize];
};

class BadIdea {
public:
  void* Alloc(size_t size) { return alloca(size); }
  void  Free(const void*)  { }
};

template <typename T>
using WhatCouldGoWrong = MySmallVector<std::pair<T, T>, 0xFFFFFFFFFFFFFFFF, BadIdea>;
« Last Edit: August 31, 2018, 12:45:31 AM by Arklon »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Using versus Typedef
« Reply #4 on: August 31, 2018, 12:59:54 AM »
@lordpalandus: Neither using nor typedef require the pre-processor. I think you're thinking of #define aliases.



@Arklon: I think I missed the point of the example here.

In particular, I don't see what is wrong with BadIdea. I assume that is just a really simple allocator class. From what I see, it's never even going to have a chance to be used. The bad idea is simply the huge initial buffer size set by the WhatCouldGoWrong alias. And also partly the misleading name MySmallVector, since the library user is free to make that huge if they wanted to by setting a large initial size.

Hmm, also just noticed the std::pair. I assume the using clause is creating an alias for a 2D vector.

This is my understanding of the code:
Code: cpp [Select]

class SimpleAllocator {
public:
  void* Alloc(size_t size) { return alloca(size); }
  void  Free(const void*)  { }
};

template <typename T, size_t InitialSize, typename Allocator>
class SmallSizeOptimizedVector {
  ...
  std::aligned_storage_t<sizeof(T), alignof(T)> initialBuffer[InitialSize];
};

template <typename T>
using RidiculouslyHuge2DVector = SmallSizeOptimizedVector<std::pair<T, T>, 0xFFFFFFFFFFFFFFFF, SimpleAllocator>;

RidiculouslyHuge2DVector<float> vector2D; // { {x1, y1}, {x2, y2}, ... }


I'm guessing the example of the usefulness of using was actually the 2D part, since that doubles up on the type. And also the ability to fill in the other details so you don't need to worry about them. Just that wasn't such a good thing in this example because it filled in bad details for one of those fields.
« Last Edit: August 31, 2018, 01:47:12 AM by Hooman »

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: Using versus Typedef
« Reply #5 on: August 31, 2018, 08:35:35 PM »
@lordpalandus: Neither using nor typedef require the pre-processor. I think you're thinking of #define aliases.



@Arklon: I think I missed the point of the example here.

In particular, I don't see what is wrong with BadIdea. I assume that is just a really simple allocator class. From what I see, it's never even going to have a chance to be used. The bad idea is simply the huge initial buffer size set by the WhatCouldGoWrong alias. And also partly the misleading name MySmallVector, since the library user is free to make that huge if they wanted to by setting a large initial size.

Hmm, also just noticed the std::pair. I assume the using clause is creating an alias for a 2D vector.

This is my understanding of the code:
Code: cpp [Select]

class SimpleAllocator {
public:
  void* Alloc(size_t size) { return alloca(size); }
  void  Free(const void*)  { }
};

template <typename T, size_t InitialSize, typename Allocator>
class SmallSizeOptimizedVector {
  ...
  std::aligned_storage_t<sizeof(T), alignof(T)> initialBuffer[InitialSize];
};

template <typename T>
using RidiculouslyHuge2DVector = SmallSizeOptimizedVector<std::pair<T, T>, 0xFFFFFFFFFFFFFFFF, SimpleAllocator>;

RidiculouslyHuge2DVector<float> vector2D; // { {x1, y1}, {x2, y2}, ... }


I'm guessing the example of the usefulness of using was actually the 2D part, since that doubles up on the type. And also the ability to fill in the other details so you don't need to worry about them. Just that wasn't such a good thing in this example because it filled in bad details for one of those fields.

It was a joke about coming up with the best way to obliterate the stack. It probably doesn't even compile, but why would you even want to? The std::pair<T, T> was just to ensure that even if you use a 1-byte size type, each element would take at least 2 bytes. And yes, the allocator would never be called in a normally-functioning program, but that's not applicable anymore when we're smashing the stack (no heap allocator would let you allocate this thing), since we might just happen to break everything in just the right way for the allocator to be called, which uses alloca, resulting in even more stack smashing.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Using versus Typedef
« Reply #6 on: September 01, 2018, 06:12:09 AM »
Ahh yes, I didn't pay much attention to the alloca call. It allocates memory on the stack, rather than on the heap. Declaring a local variable of that type would definitely exhaust stack space. :P