Skip to main content
Even more consistent safety parenthesis.
Source Link
Morwenn
  • 20.2k
  • 3
  • 69
  • 132
  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The define_vector_##T macro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be a static variable in a function. Not that this is a good idea either, but you get it...).

  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) directly instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • The macros parameters are not always correctly guarded with parenthesis. If I have a stack-allocated vector and try to give its address to a function (e.g. begin(&vec_i)), it fails miserably. More parenthesis are needed:

      #define begin(collection) \
              (collection)->_functions->begin(collection)
    
  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*((collection)->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

  • The define_vector_##T macro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be a static variable in a function. Not that this is a good idea either, but you get it...).

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*(collection->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

  • The define_vector_##T macro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be a static variable in a function. Not that this is a good idea either, but you get it...).

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The define_vector_##T macro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be a static variable in a function. Not that this is a good idea either, but you get it...).

  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) directly instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • The macros parameters are not always correctly guarded with parenthesis. If I have a stack-allocated vector and try to give its address to a function (e.g. begin(&vec_i)), it fails miserably. More parenthesis are needed:

      #define begin(collection) \
              (collection)->_functions->begin(collection)
    
  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*((collection)->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

Note about names prefixed by an underscore in the global scope.
Source Link
Morwenn
  • 20.2k
  • 3
  • 69
  • 132

I did manage to improve some things since the question was posted. So here is what I discovered, that could somehow improve the implementation and the usability of the Vector(T):

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*(collection->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

  • The define_vector_##T macro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be a static variable in a function. Not that this is a good idea either, but you get it...).

I did manage to improve some things since the question was posted. So here is what I discovered, that could somehow improve the implementation and the usability of the Vector(T):

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*(collection->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

I did manage to improve some things since the question was posted. So here is what I discovered, that could somehow improve the implementation and the usability of the Vector(T):

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*(collection->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

  • The define_vector_##T macro introduces names that begin with an underscore in the global scope. Such names are reserved to the implementation; they should either use another prefix or be put somewhere else (for example, the function table could be a static variable in a function. Not that this is a good idea either, but you get it...).

Extra layer of parenthesis in macro.
Source Link
Morwenn
  • 20.2k
  • 3
  • 69
  • 132

I did manage to improve some things since the question was posted. So here is what I discovered, that could somehow improve the implementation and the usability of the Vector(T):

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*(collection->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

I did manage to improve some things since the question was posted. So here is what I discovered, that could somehow improve the implementation and the usability of the Vector(T):

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          *(collection->_functions->front(collection))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

I did manage to improve some things since the question was posted. So here is what I discovered, that could somehow improve the implementation and the usability of the Vector(T):

  • First of all, there was an error in the code. Shame on me. I shared a piece of code that could not be compiled. I must have posted a version that was not up-to-date. The macro new does not work for two reasons: the token pasting is not well done and I forgot to add the parenthesis for the function call. The first solution to fix that error is to have new(Vector(int)) call new_Vector_int():

      #define new(type) \
          PASTE(new_, type)()
    

    The second solution is to have new(Vector(int)) resolve to new_Vector_int and add the call by hand on the calling site. This would allow to "overload constructors":

      #define new(type) \
          PASTE(new_, type)
    
      Vector(int)* vec_i = new(Vector(int))(/* maybe more paremeters later */);
    
  • The typedef between struct _vector_##T and Vector_##T is done quite late. I could have used the "forward typedef":

      typedef struct _vector_##T Vector_##T;
    

    This would have allowed me use Vector_##T instead of struct _vector_##T in the function declarations.

  • Actually, I can also use Vector(T) instead of Vector_##T in the body of define_Vector. Thanks to that, I can drop many ## operators and the code ends up being easier to read.

  • Last but not least, I kind of failed (at first) to mimic the C++ functions returning lvalues (non-const references). I can still work around that by having the functions return pointers and having the interface macros dereference those functions returns. Take the example of front. The declaration becomes:

      T* vector_front_##T(const Vector(T)*);
    

    The definition becomes:

      T* vector_front_##T(const Vector(T)* vector)
      {
          return vector->_data;
      }
    

    And the macro front becomes:

      #define front(collection) \
          (*(collection->_functions->front(collection)))
    

    This new trick allows us to get rid of the workaround macros elem, first and last since at, front and back can now somehow return lvalues instead of simple values, and not only for data structures with contiguous storage. Of all the points, this is the only one that allows to increase the usability of the code.

Reworded the answer.
Source Link
Morwenn
  • 20.2k
  • 3
  • 69
  • 132
Loading
Source Link
Morwenn
  • 20.2k
  • 3
  • 69
  • 132
Loading