Saturday, 26 July 2014

Python's in in c++ - Part 1: Expression Templates

A few years ago, my colleague and I, we had started Python project in our work. We were both hardcore c++ programmers, so it was a new experience. After learning Python's syntax and writing some code, we had compared ours c++ and Python insights. One nice feature in Python is consistency and its keyword in. If You want to check if some object o is in collection C, You only have to write:
if o in C: # code...
Similarly, iterating over any collection in Python is straightforward:
for e in C: # code... 
But in c++, it is complicated. Simple iteration has the form:
for(type_of_collection::iterator it=c.begin(); it!=c.end(); ++it) { /* code... */ }
We have been discussing c++'98 back then, so a new for loop was not in standard yet. Today, in c++11, You can write:
for(auto& o : C) { /* code... */ }
But the other use-case for Python's in keyword has not been addressed by c++ standard. Of course, You can use standard functions and methods, but it is not as consistent as in Python:
std::find(begin, end, value) // for any range
std::set<k>::find(value), std::map<k v="">::find(key) // for associative collections
std::binary_search(begin, end, value) // for sorted, random access collections
Lack of consistency is some kind of trade-off: by using different signatures You can test whether some object is in collection and obtain iterator to it in one pass over the collection. Once more, c++ chooses efficiency instead of simplicity. ;)

Back then I have thought that creating something similar to Python's in keyword in c++ would be a nice exercise. Sure, by using such construct ''in a Python's way'' You will sacrifice ability to test if element is in collection and obtain iterator to it in one pass over the collection. I was well aware of that trade-off. One evening spent on coding and prototype was ready. Few days ago, I recalled about my old code and decided to modernize it and show it on this blog. Full source code can be found on GitHub in 1.0.0 tag. Presented code is still a prototype and shouldn't be used in production code.

First, I will show usage of my solution. Although I could have written some unit tests, I decided not to do it. It is prototype and console output will suffice. The only source file looks like this:
#include <iostream>
#include <array>
#include <vector>
/* And includes for other standard collections: list, set, unordered_set, map, unordered_map */

// Has to go after standard headers...
#include "in.hpp"

template<typename C>
void check(const int e, const C& c) {
 std::cout
  << "\t" << e << ((e in c) ? " is" : " is not")
  << " in given collection: " << typeid(c).name() << std::endl;
}

int main() {
 std::cout << "Checking raw array:" << std::endl;
 int array[] = { 3, 2, 7, 10 };
 check(50, array);
 check(2, array);
 check(10, array);
 check(1, array);

 std::cout << "Checking vector:" << std::endl;
 const std::vector vec = { 3, 2, 7, 10 };
 check(50, vec);
 check(2, vec);
 check(10, vec);
 check(1, vec);

 std::cout << "Checking map:" << std::endl;
 const std::map om = { { 3, "three" }, { 2, "two" }, { 7, "seven" }, { 10, "ten" } };
 check(50, om);
 check(2, om);
 check(10, om);
 check(1, om);

 /* In the same way other standard collections are tested. */
}
There is a lot of code compared to one tiny line with in ''keyword'' in function check, which tests whether element is in collection and prints proper message to standard output. But how the c++'s in is working?

First of all, in is defined as a macro in in.hpp file:
#define in << ::mad::cppin::in_ <<
So, the macro only adds calls of operator << for object mad::cppin::in_. This object is instance of empty type in_type, for which two operator << are defined:
struct in_type {};

template<typename Element>
details::in_left<Element> operator<<(const Element& e, const in_type) {
 return details::in_left(e);
}

template<typename Collection>
details::in_right<Collection> operator<<(const in_type, const Collection& c) {
 return details::in_right(c);
}
If the in_ object is on the right hand side of an binary shift expression, then instance of details::in_left is returned in which value of other parameter of binary shift is stored. Similarly, if the in_ object is on the left hand side of binary shift operator, the instance of in_right class is returned. Both classes are defined as follows:
template<typename Element>
class in_left {
public:
 in_left(const Element& e) : e_(e) {}

 template<typename Collection> friend
 in_impl<Element, Collection> operator<<(const in_left<Element>& l, const Collection& c) {
  return in_impl<Element, Collection>(l.e_, c);
 }
private:
 const Element& e_;
};

template<typename Collection>
class in_right {
public:
 in_right(const Collection& c) : c_(c) {}

 template<typename Element> friend
 in_impl<Element, Collection> operator<<(const Element& e, const in_right<Collection>& r) {
  return in_impl<Element, Collection>(e, r.c_);
 }
private:
 const Collection& c_;
};

} // details
The purpose of above presented classes is to store arguments to the binary shift operators and remember side of binary shift operator, on which parameter was. If parameter was on the left side it is considered as an object to be searched for. If it was on the right hand side, then it is treated as a collection to be search in. Both classes define operator << for remaining part of the in expression. If the binary shift operator is called, the instance of in_impl class is returned. It is simple application of the expression templates idiom. The in_impl class is shown below:
template<typename Element, typename Collection>
class in_impl {
public:
 in_impl(const Element& e, const Collection& c)
  : c_(c), e_(e) {}

 explicit operator bool() const {
  return find_impl(e_, c_);
 }
private:
 const Collection& c_;
 const Element& e_;
};
It only stores its arguments and in context of boolean expression (such as if statement) returns result of function find_impl call. In this function the magic happens, which enables efficient working with different types of collections. The magic will be described in part 2.

Saturday, 12 April 2014

My adventures with Ubuntu on Hyper-V. Part 3.

As I was forced to revisit my Hyper-V and Ubuntu configuration earlier this week, today I have decided to try run Ubuntu in fullscreen one more time. After searching the Internet I have found working solution on Ask Ubuntu. On my configuration it required two steps:

  1. Installing linux-image-extra-virtual package
    sudo apt-get install linux-image-extra-virtual
  2. Updating GRUB's configuration file (/etc/default/grub) by changing one line:
    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=hyperv_fb:1680x1050" 
  3. Updating GRUB' configuration by executing the command:
    sudo update-grub
After rebooting Ubuntu I can finally use it in fullscreen mode!

Thursday, 10 April 2014

Windows Universal Apps

I had plenty of work with my PhD studies and my job so I had not had time to install Visual Studio Express. But on the Build conference, Microsoft showed new Windows' features. Updated Visual Studio can create universal apps for both: Windows 8.1 and Windows Phone 8.1! Although I have not had time to tinker with it long enough, I have managed to create a universal hub app, build it and run on Windows Phone Emulator. And it worked! But after looking into Solution Explorer:
 I have noticed that the Universal Windows Apps are not something entirely new. They are based on an idea, which was known at least from the time when Visual Studio 2013 was published! It was presented on MSDN: share common code and use a separate project for presentation only. Updated Visual Studio mostly eases create of the separate project and shared library and debugging code for the Windows Phone. Although it is not a revolution but an evolution of ideas known for some time, it is welcomed. Maybe it would allow consistent experience across different Windows platforms and faster apps delivery!

Wednesday, 9 April 2014

My adventures with Ubuntu on Hyper-V. Part 2.

Yesterday I upgraded Windows 8 and installed Visual Studio Express 2013 Update 2 RC. Today I tried to run Ubuntu on Hyper-V. It worked but without an Internet connection... The first thing which I have done, it was to search the Internet to see if upgrading Windows could break something with Hyper-V. I have found nothing...

Searching for problems with Hyper-V itself and changing network setting also was no good. I was ready to give up when I have remembered that I have also installed Visual Studio with Windows Phone Emulator. Running Emulator for the first time created a virtual network adapter and after its creation, I was able to reconfigure Hyper-V and restore Internet connection in Ubuntu. Why without running the emulator it was impossible to configure networking in Hyper-V is still a mystery for me... The installer could have created a network adapter during the installation process and not delay this step to first execution of emulator...

Wednesday, 20 November 2013

My adventures with Ubuntu on Hyper-V. Part 1.

I really like Windows 8. I like Metro Modern UI, tiles and the new Start Screen. And I like playing games. That's why I use Windows on my personal hardware. But in my workplace I use Ubuntu. On my old laptop, I have used the dual boot. But it was annoying to constantly synchronise mails, chats, login data and files... And I always had a slow Internet connection, so downloading two copies of everything was painfully slow...

When I was buying a new machine, I have decided it has to be powerful enough to run a virtual machine. I've bought a laptop with 32GBs of RAM and a Core i7 processor. And I wanted to tinker with Windows Phone apps, so I have upgraded Windows 8 to Pro version to use Hyper-V. I have followed instructions on how to install Ubuntu on Hyper-V and it worked. Almost. When I have used "external virtual switch" my host Windows 8 system lost Internet connection. Even, if I have checked the checkbox for sharing the connection. But it was half of a success: guest systems could connect to the Internet.

I have played with settings for some time and finally using "internal virtual switch" with setting sharing my Wi-Fi from Control Panel worked. If only I could use Ubuntu in full screen with utilising all my monitor's pixels...