Demo Use Case For N4027: has_member_function(“MyFunction”)   Project: Programming Language C++ ­ SG7 Reflection  Author: Andrew Tomazos   Date: 9 June 2014 

Demo Use Case For N4027: has_member_function(“MyFunction”)  Objective  Implementation  1. Some Helper Traits and Functions  2. Member Predicate  3. Name Index  4. The Function Itself  Conclusion 

Objective   We show how to build upon the N4027 reflection primitives to define a constexpr function:    template   constexpr bool has_member_function(const char* name);    such that has_member_function(“bar”) will return true iff the class Foo contains a  direct public non­static member function with the name bar.    For example, for the class:    struct MyClass  {  void foo();  int qux;  static void quux();  static int quuux;  void bar();  private:  void priv();  public: 

void baz();  };    The following static assertions will succeed:    static_assert(has_member_function("foo"), "");  static_assert(has_member_function("bar"), "");  static_assert(has_member_function("baz"), "");    static_assert(!has_member_function("nope"), ""); // no such  member  static_assert(!has_member_function("qux"), ""); // qux is  variable  static_assert(!has_member_function("quux"), ""); // quux is  static  static_assert(!has_member_function("quuux"), ""); // quuux  is static variable  static_assert(!has_member_function("priv"), ""); // priv is  private    You can also use has_member_function at run­time:    int main(int argc, char** argv)  {  if (has_member_function(argv[1]))  std::cout << "MyClass has member function " << argv[1] <<  std::endl;  else  std::cout << "MyClass does NOT have member function " <<  argv[1] << std::endl;  };    The index of the member function names is constructed at compile­time as part of the pure  library demo code, it is not provided by a compiler extension.  It consists of a sorted array of the  function names so lookup is O(logN) (either at compile­time if evaluated as part of a constant  expression, or run­time if not)   

Implementation  

1. Some Helper Traits and Functions  

First we add some pure library helper templates.    pointee_type

 gives the pointed to type of a pointer or pointer­to­member.  That is, for P=T* it  gives T, for P=T C::* it gives T.    template struct pointee_type;  template struct pointee_type { using  type = T; }; // for pointer­to­members (non­static members)  template struct pointee_type { using type = T; }; //  for pointers (static members)    is_static

 gives the staticness of a pointer type given by std::class_member_pointer.  If P=T*  the member is static, if P=T C::* the member is non­static:    template bool is_static;  template constexpr bool is_static =  false;  template constexpr bool is_static = true;    Next we add a function to compare string literals (compile­time strings).  It is a constexpr version  of std::strcmp:    constexpr int strlitcmp(const char* a, const char* b)  {       for (size_t i = 0; true; i++)       {           if (a[i] == b[i])           {               if (a[i] == 0)                   return 0;               else                   continue;           }             if (a[i] < b[i]) return ­1; else return +1;       }  }   

2. Member Predicate   We are now ready to define a predicate on which we are going to filter the members of the class.  We are interested in public, non­static, functions.  We simply define the predicate as the 

conjuction of these three properties.  is_pnsmf is true iff the Ith direct member of C is a  public non­static member function:    template  constexpr bool is_pnsmf =      /* public */ std::class_member_access_level_v ==  std::public_access &&      /* non­static */ !is_static::value_type> &&      /* function */ std::is_function::value_type>::type>::value;   

3. Name Index   Now we want to create an index of the names of the members that satisfy the predicate.  First  we need to calculate the storage requirements.  We define a pnsmf_index_dim_t class to hold  the number of strings and the number of total chars we need in the index:    struct pnsmf_index_dims_t  {  size_t nstrings = 0;  size_t nchars = 0;  };    We then make a pass over the members to calculate these requirements.  We use  std::make_index_sequence and calculate the dimensions:    template  constexpr pnsmf_index_dims_t  get_pnsmf_index_dims(std::index_sequence)  {      size_t nstrings = 0, nchars = 0;        for (bool b : { is_pnsmf... })          nstrings += b;        for (size_t x : { (is_pnsmf ?  std::class_member_name::size + 1 : 0)... } )  nchars += x;        return {nstrings, nchars}; 

}    We then assign the dimensions to a static constexpr pnsmf_index_dims variable template:    template constexpr auto pnsmf_index_dims       =  get_pnsmf_index_dims(std::make_index_sequence>());    Now that we know our space requirements statically we can go ahead and define our index data  structure pnsmf_index_t.  It holds an array of the character data for the strings, and an array of  offsets within that array where each string starts.  The offsets will be sorted in lexigraphical order  so the index can be binary searched.  We will mention you can use a hash table instead here,  but to keep the demo simple we just use a sorted array.    template   struct pnsmf_index_t  {  static constexpr size_t nstrings = nstrings_in;  static constexpr size_t nchars = nchars_in;  size_t stroff[nstrings] = {0};  char chardata[nchars] = {0};  };    Now we define a function that builds the index for each class.  We make another pass over the  members and store those that satisfy the predicate in our index.  We then sort the index.  For  sorting we have used O(n^2) bubblesort for simplicity, but you can easily imagine how to use  mergesort or quicksort here with just a few more lines of code:    template  constexpr pnsmf_index_t.nstrings,  pnsmf_index_dims.nchars>  get_pnsmf_index(std::index_sequence)  {  constexpr auto dims = pnsmf_index_dims;    pnsmf_index_t ret;    size_t current_stroff = 0;  size_t current_chardata = 0;    for (const char* s : { (is_pnsmf ?  std::class_member_name_v : nullptr)... } ) 

if (s)  {  ret.stroff[current_stroff++] = current_chardata;  for (const char* p = s; true; p++)  {  ret.chardata[current_chardata++] = *p;  if (!*p)  break;  }  }    bool ok = true;  do  {  ok = true;  for (size_t i = 0; i+1 < dims.nstrings; i++)  if (strlitcmp(ret.chardata +  ret.stroff[i],ret.chardata + ret.stroff[i+1]) >= 0)  {  size_t t = ret.stroff[i];  ret.stroff[i] = ret.stroff[i+1];  ret.stroff[i+1] = t;  ok = false;  }  }  while (!ok);    return ret;  };    Then we create a static constexpr variable template pnsmf_index that is initialized with the  built index for each class on instantiation:    template constexpr auto pnsmf_index       =  get_pnsmf_index(std::make_index_sequence>());   

4. The Function Itself   Now that we have built the index, we can define the has_member_function function that  performs a binary search of it: 

  template   constexpr bool has_member_function(const char* name)  {  constexpr auto& idx = pnsmf_index;    size_t a = 0, b = idx.nstrings;    while (a != b)  {  size_t c = a + (b­a) / 2;  switch (strlitcmp(name, idx.chardata + idx.stroff[c]))  {  case 0: return true;  case ­1:  b = c;  break;  case +1:  a = c+1;  break;  }  }    return false;  }    and we’re done. 

Conclusion   We should note that this use case is not a primary one for N4027 or for reflection in general.  Also, we want to make clear that this is just a demo, for production purposes we anticipate  production quality non­standard reflection libraries will be built atop N4027 that have more  sophistication than the quick direct demo indexing we have shown here.  Our purpose for this  demo is to prove that all the necessary information is exposed by our minimal compile­supported  traits, and that it is possible to use it efficiently from pure library code.    The code in this paper has been tested, and the code under Goal works as shown verbatim.   

Demo Use Case For N4027: has_member_function ... -

First we add some pure library helper templates. ... We are now ready to define a predicate on which we are going to filter the members of the class.

182KB Sizes 1 Downloads 137 Views

Recommend Documents

No documents