← Back to team overview

maria-developers team mailing list archive

Re: Code syntax: questions on pointers, etc.

 

Hi, Aleksey!

On Oct 23, Aleksey Midenkov wrote:
> On Sun, Oct 22, 2017 at 10:27 PM, Sergei Golubchik <serg@xxxxxxxxxxx> wrote:
> >> 2. It is still passed as a pointer to functions. Why is that?
> >>
> >> The main feature of C++ references is that it cannot be NULL, so we
> >> get segfault on top of the stack (closer to a cause), not the
> >> bottom of it. I see that pointers are now widely used and mainly
> >> assumed to be always non-NULL (i.e. dereferenced without
> >> assertion). But placing such implicit contract on data is not
> >> evident and bug-prone. IMHO it's much better to use references
> >> whenever it is possible (and when there is no need in cosy NULL
> >> semantic). What do you think?
> >
> > This is something that gets raised over and over.
> > Some prefer C-style pointers over references, so that when you look at
> > the function call:
> >
> >    func(a,&b);
> >
> > you can immediately see that `a` is passed by value and cannot be
> > modified by `func`, while `b` can.
> >
> > Others prefer C++ references.
> 
> But are the reasons mentioned above not enough to once and for all
> resolve the dilemma?

As we're talking about it (and were, many times) - apparently not :)

> > I personally reside on the middle ground, where one uses pointers to
> > pass "out" parameters, and const references for "in" parameters.
> >
> >> But for such lightweight structs like LEX_CSTRING it is even better
> >> to pass by value, so we could have the conventience of type cast.
> >
> > What do you mean by that?
> 
> #include <cstring>
> 
> struct LEX_CSTRING
> {
>   const char *str;
>   unsigned long length;
>   LEX_CSTRING() {}
>   LEX_CSTRING(const char *c_str)
>   {
>     str= c_str;
>     length= strlen(c_str);
>   }
> };
> 
> void func(LEX_CSTRING arg)
> {
> };
> 
> class MyCunningString
> {
> public:
>   operator LEX_CSTRING() { return LEX_CSTRING(); }
> };
> 
> int main()
> {
>   MyCunningString str;
>   func(str);
>   const char* c_str;
>   func(c_str);
>   return 0;
> }

Okay. Looks good.

> >> 3. LEX_CSTRING and LEX_STRING are now non-convertible. Why not to make:
> >>
> >> template <typename char_t>
> >> struct st_mysql_lex_string
> >> {
> >>   char_t *str;
> >>   size_t length;
> >> };
> >>
> >> typedef st_mysql_lex_string<char *> LEX_STRING;
> >> typedef st_mysql_lex_string<const char *> LEX_CSTRING;
> >> typedef st_mysql_lex_string<const unsigned char *> LEX_CUSTRING;
> >>
> >> ?
> >
> > What would that change?
> 
> #include <cstring>
> 
> template <typename char_t>
> struct st_mysql_lex_string
> {
>   char_t *str;
>   size_t length;
>   st_mysql_lex_string<char_t>(){}
>   template <typename charX_t>
>   st_mysql_lex_string<char_t>(st_mysql_lex_string<charX_t> &from) :
>     str ((char_t *) from.str),
>     length (from.length) {}
> };
> 
> typedef st_mysql_lex_string<char *> LEX_STRING;
> typedef st_mysql_lex_string<const char *> LEX_CSTRING;
> typedef st_mysql_lex_string<const unsigned char *> LEX_CUSTRING;
> 
> void func(LEX_CSTRING arg)
> {
> }
> 
> int main()
> {
>   LEX_STRING str;
>   func(str);
>   LEX_CUSTRING ustr;
>   func(ustr);
>   return 0;
> }

Right, so I thought. So template<> doesn't change anything.
Constructors and passing-by value do.

Anyway, type conversion is nice. It's rather annoying to cast between
LEX_CSTRING and LEX_STRING, for example.

Templates - not sure we could use them here, because these types
sometimes need to work in pure C too. LEX_STRING does, at least.
We could do, say

  struct LEX_STRING {
    char *str;
    size_t length;
  #ifdef __cplusplus
    LEX_STRING(...) { ... }
  #endif
  }

to have some extra C++ convenience. But with templates you'd need a
completely separate LEX_STRING definition in C and C++.

Regards,
Sergei
Chief Architect MariaDB
and security@xxxxxxxxxxx


References