Wednesday, 30 October 2013

Some basic decltype-SFINAE


In this blog I'd like to give you the impression that I'd know everything ;-), but, alas, that's not true. Mainly because with the advent of C++11 there's a lot of things for me to learn. You may say I'm late to the party, and yes, I was somehow lazy playing (or rather not playing:) with C++11 compilers. That was because my clients didn't want to use the newest features yet (you know, a bunch of conservative ... etc.). But now, all of a sudden, I can use every C++11 and Boost feature I want in my daily work (yay!!!), and there's definitively a difference between playing with something and using it on a daily basis!

Back to the things I should learn. There are many of them, but we'll take the decltype-SFINAE as an example. The what-SFINAE? Or: the decltype-what?? Or, even worse: the what-what???

So what is SFINAE really? You can google it, or you can just take my word that SFINAE works by purposely introducing compile errors (thus the SF part in the name) which will be then ignored by compiler, (the INAE part in the name) just to remove one of template specializations to suit your purpose!

The idea is that for one class you'll have one set of template definitions (where the unwanted ones will be ignored by compiler), and for an another class a different one. Simple, isn't it? ( BTW as I'm getting older and older I came to recognize that most of the human ideas are simple, but some of them just aren't clearly stated; that seems to be an ego-thing for some people...)

So what is the decltype-SFINAE? Simple again, it's using SFINAE in the C++11 auto+decltype syntax for function declarations. Like here:
  template <class T> auto xxx() -> decltype(XXX, string) { ... }
How's that? Remember, decltype takes an expression as argument, and comma operator in expressions renders the last part as the value, so everything before the comma (i.e. the XXX thing) can be used to fool the compiler into/out of SFINAE. Tada!

When I first saw this technique in action*, used for detecting if some method is defined in a given class, there was an utility test class defined:
  template<typename T> struct has_size_method 
  {  
    //...

    template&ltclass C> static yes test(SFINAE<C,&C::size>*); // won't explain this impl.!

  public:
    static constexpr bool value = std::is_same<decltype(test<T>(nullptr)),yes>::value;
  };
and then it could be used like that:
    cout << has_size_method<ClassXXX>::value;
I wanted the same functionality (i.e. detection of a special optional method) but I didn't want another utility class. Frankly speaking, I'm more for the direct approach, not for burying simple concept under irritating levels of indirections. I a word, there had to be a better way! So I tried this:
  // got get_channel_id()?
  template <typename T, typename U>
  auto mk_object_name(T* obj, U prefix) 
    -> decltype(std::declval<T>().get_channel_id() == 1, std::string()) 
  {            
    return format_strg(prefix * 1000 + obj->get_channel_id(), std::hex, typeid(*obj).name());
  }
  
  // nope!
  template <typename T, typename U>
  std::string mk_object_name(T* obj, U prefix, T* obj2 = nullptr) 
  {
    return format_strg(prefix, std::hex, typeid(*obj).name());
  }
The first overload will be removed by SFINAE if the type T doesn't have the get_channel_id() method, the second one will be always present in the normal case, and let's hope that it's sufficiently different for the first one. Simple, isn't it? Then I used it like this:
  struct XXX {
    int get_channel_id() { return 1; } 
  };
  struct YYY {
    int get_YYY_id() { return 1; } 
  };

  main()
  {
    XXX x;    
    std::string nameX = mk_object_name(&x, 100);
    std::cout << nameX << std::endl;

    YYY y;    
    std::string nameY = mk_object_name(&y, 200);
    std::cout << nameY << std::endl;
 }
Alas, it didn't work for the XXX struct. That is what comes from hoping that the templates will just match! The obvious problem here is, that in this case both overloads are enabled. You did spot it at once? Sorry, I said this is a post about basic(!) decltype-SFINAE! So what can we do to hide the base version if the extended version is possible? A template function for dispatching to the desired overload proved to be sufficient:
  // actual functions
  template <typename T, typename U>
  auto mk_object_name_helper(T* obj, U prefix, int) 
    -> decltype(std::declval<T>().get_channel_id() == 1, std::string()) 
  {            
    return format_strg(prefix * 1000 + obj->get_channel_id(), std::hex, typeid(*obj).name());
  }

  template <typename T, typename U>
  std::string mk_object_name_helper(T* obj, U prefix, long) 
  {
    return format_strg(prefix, std::hex, typeid(*obj).name());
  }

  // "dispatching" function
  template <typename T, typename U>
  std::string mk_object_name(T* obj, U prefix) 
  {
    return mk_object_name_helper(obj, prefix, 0);
  }
And then it worked like a charm**. I think, it's not too much overhead when compared to the has_size_method template shown above. The dispatching function just uses the fact that int is a better overload for zero than long.

You can compile the above code online, for example at http://gcc.godbolt.org/ use g++-4.8 with the -std=c++11 option. The nice thing there is that in the assembler window you can see what template overload was selected. Additionally you'll see the famous template bloat in action! It's fun! If you'd only like to let it run online, try http://liveworkspace.org/.

PS: And what was it all for? Did you get it? We were generating object names for various "software device" types, some supporting only the board IDs, some offering additional support for channels.

--
* look for example here: http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/

** There's another way, of course! Facebook's Folly library accomplishes it with enable_if and the pair of is_same and !is_same in the definition of the return value of a function:
  template <class T>
  typename std::enable_if<
    HasLockUnlock<T>::value && !std::is_same<T, boost::shared_mutex>::value>::type
  acquireRead(T& mutex) {
    mutex.lock();
  }
  //Special case for boost::shared_mutex.
  template <class T>
  typename std::enable_if<std::is_same<T, boost::shared_mutex>::value>::type
  acquireRead(T& mutex) {
    mutex.lock_shared();
  }
 Look here for overview of how the classic enable_if<> can be used. 

Thursday, 17 October 2013

How does C++11 bugfixing in C++14 look like?


Maybe you've heard it already, but the planned C++14 standard will be a small, bugfixing release for the C++11 specification and not a true next version of C++.

Well, not really (see here), there are a couple of interesting genuine new features added! For example: generic lambdas (yay! see my old post...), lambda capture initialization expressions, function return type deduction (i.e. auto on functions), templates for variables, runtime-sized arrays (int arr[varX]), binary literals (011100b), shared mutexes and locks, std::optional, standard user-defined literals (like "xxx"s for std::string), std::dynarray, tuple access via type (get<int>(tupleX)). I don't know what you think, but for me that's quite a list of new features (and that's not a complete list) to be digested!

But some of the changes are real bugfixes. For myself, I couldn't imagine what bugfixing could be supposed to mean - are there bugs in the standard? With best C++ gurus working on that? Well, unfortunately there are! One of them is the absence of std::make_unique() counterpart to the std::make_shared(). What is is the reason for that? Herb Sutter explains:
" That C++11 doesn’t include make_unique is partly an oversight, and it will almost certainly be added in the future. "
 What, an oversight! But beacuse of that you cannot write a safe code for multiple resource initialization using std::unique_ptr! That (and hearing them speaking on Going Native 2013) convinces me that the gurus are just humans and developers like you and me!

Another case in point is taken from a discussion on complex initialization idiom. Here's the code:
  const int i = [&]{
    int i = some_default_value;
    if(someConditionIstrue)
    { 
      // Do some operations and calculate the value of i;
      i = some calculated value;
    }
    return i;
  } () // note: () invokes the lambda!

Well, it looks absolutely innocuous (and kind of JavaScript like!), and it will even compile. But Andrzej Krzemieński pointed out that:
I believe it is not a valid C++11 code. According to sect 5.1.2 para 4, when the body of the lambda is not a simple return statement its deduced return type is void. While the expectation that this example would work is logical, and likely acceptable in C++14, C++11 seems to refuse it.
And you know what? Herb explains:
The committee has already pretty much agreed that this should be allowed (which is why our compiler also already supports it, and with GCC and Clang that would make it de facto portable), and this extension is likely to be voted into the C++ working paper soon (this month, or September).
Thus making it a classic example of bugfixing! Well, not really, the standard says that lambda return type can be deduced, but only when there is exactly one statement, and that statement is a return statement that returns an expression. OK, but compilers can do much more than that already, so it's an unnecessary restriction we can do away with! Thus in this case it's not really an oversight, but rather lack of time to process feedback from compiler writers. Bjarne was always saying that the committee is doing unpaid work and that mostly in their free time.

Update:

Ooops, I forgot that std::optional and std::dynarray were moved out of the standard and parked in TS.  Thanks for correction goes to @meetingcpp!

Thursday, 10 October 2013

Some life hacking.


As I started several C++ blog posts but cannot finish a single one, there's definitely time for some some life-changing advice! This couple of lines from a great post seemed adequate:
Make a Habit of Making Things 
Too many people die with their best ideas still inside of them. 
Your legacy is what you share, not what you know or harbor within yourself. Unshared knowledge is like potential energy. It’s great to have, but it will never do anything unless you turn it into something else. 
Turn your knowledge into a book. Turn your inspiration into art. Turn your words into music. Turn your ideas into a business. Build something. Write something. Create something.  
... 
You can either be judged because you created something or ignored because you left your greatness inside of you. Your call.
I just let it uncommented as reminder and reference.