Outpost Universe Forums

Off Topic => Computers & Programming General => Topic started by: Hooman on August 30, 2018, 01:15:40 PM

Title: Using versus Typedef
Post by: Hooman 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::*)();

Title: Re: Using versus Typedef
Post by: lordpalandus on August 30, 2018, 01:48:42 PM
Is the keyword "using" a preprocessor, like a "typedef" is?
Title: Re: Using versus Typedef
Post by: leeor_net 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.
Title: Re: Using versus Typedef
Post by: Arklon 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>;
Title: Re: Using versus Typedef
Post by: Hooman 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.
Title: Re: Using versus Typedef
Post by: Arklon 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.
Title: Re: Using versus Typedef
Post by: Hooman 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