Thursday, May 31, 2012

Generic member function chaining in C++

Recently, I found myself wondering how I could set up something like this in C++:

DeferredCalls<SomeClass> d(new SomeClass);
d.defer<void>(&SomeClass::SomeVoidReturningMember)
  .defer<int>(&SomeClass::SomeIntReturningMember, &someFuncToValidateReturnValue)
  .defer<float>(&SomeClass::SomeFloatReturningMember, &someFuncToValidateReturnValue, someParameter);


d.callChain(); // or d() if using operator()();


Google gave me almost nothing, mostly because I wasn't sure about what to Google. I did, however, find an interesting, heavily templated, approach that the project LuaBrigde uses to enable calling of arbitrary member functions from Lua. After skimming through the code, I decided that it should be possible for me to do something similar. This is how I did it.


Disclaimer: If you don't like templates, you have been warned.


First, we need a factory class to enable us to do what I described above. Let's start with a member function that has no parameters and some arbitrary return value.


template <class T>
class DeferredCallFactory {
  public:
    explicit DeferredCallFactory(T * const t) : t_(t) {}


    template <typename Ret, typename Sig = Ret(T::*)()>
    DeferredCallFactory &deferCall(Sig fp) {
      //do something
      return *this;
    }
  private:
    T * const t_;
};


Simple enough, right? However, we need some object to hold information about the call we want to (later) make. Let's stop for a while and think about this. The information holding object(s) will also need to be templated as we want to support several different types of member functions. But we also need to store these objects somewhere, and since they will be templated (differently!) we can't just use a homogenous STL-container like std::list. This is where one of the limitations of the system comes into play. If we do the obvious, use a non-templated base class which every other information holder inherits from, we can use any homogenous container we want. But any virtual methods we implement cannot be templated. Do you see where I'm going with this?


class DeferredBase {
  public:
    virtual void call() = 0;
};


Now, there's our problem. We can't capture return values (if we don't overload that call method with every conceivable return type) with this approach. So we need to invent something that allows us to at least inspect what the chained functions are returning. More on that later, let's continue with the most basic Deferred.


template <class T, typename Ret, typename Sig>
class DeferredCall : public DeferredBase {
  public:
    DeferredCall(T * const t, Sig method) :
      t_(t), m_(method) {}


    virtual void call() {
      (t_->*m_)();
    }
  private:
    T *const t_;
    Sig m_;
};


Now we modify the factory, first adding a container:


std::list<DeferredBase *> deferred_;


Then implementing the deferCall stub:


template <typename Ret, typename Sig = Ret(T::*)()>
DeferredCallFactory &deferCall(Sig fp) {
  deferred_.push_back(
    new DeferredCall<T, Ret, Sig>(t_, fp)
    );
  return *this;
}


Now we have a system that can chain calls to arbitrary member functions with the signature
ReturnValue (ClassType::*)(void)


Let's make it more interesting by adding validator functions. Time for a new derived class.


template <class T, typename Ret, typename Sig, typename Vald>
class DeferredCallWithValidator : public DeferredBase {
  public:
    DeferredCallWithValidator(T * const t, Sig method, Vald validator) : t_(t), m_(method), v_(validator) {}


    virtual void call() {
      Ret val = (t_->*m_)();
      v_(val);
    }
  private:
    T * const t_;
    Sig m_;
    Vald v_;
};


And then we modify the factory to support this new deferred.


template <typename Ret, typename Sig = Ret(T::*)(), typename Vald = void(*)(Ret)>
DeferredCallFactory &deferCall(Sig fp, Vald v) {
  deferred_.push_back(
    new DeferredCallWithValidator<T, Ret, Sig, Vald>(t_, fp, v)
    );
  return *this;
}


Now were golden! This approach is a breeze to generalize further, the next step being to add DeferredXYZ-classes that support templated parameters to the member function calls.


Finally, some syntactic sugar:


//(in DeferredCallFactory)
void operator()() {
  std::list<DeferredBase *>::const_interator it;
  for (it = deferred_.begin(); it != deferred_.end(); ++it) {
    (*it)->call();
  }
}


What's the use case you wonder? In my case it's being able to specify calls to an API under test in a nice way at one point in the program, and then being able to make those calls (multiple times), by just writing:


factory();


The boilerplate code for actually doing this is horrible, but when it's done you don't have to see it anymore.

Tuesday, October 4, 2011

Updated Spotify ebuild (0.6.1.309)

Earlier I posted an ebuild to Spotify (for the package manager in Gentoo called Portage). Here is a newer version of that ebuild. Like the last time, put it in a file called spotify-0.6.1.309.ebuild inside your local repository (like /usr/local/portage/media-sound/spotify/)

# Copyright 1999-2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

EAPI="2"

DESCRIPTION="Spotify desktop client"
HOMEPAGE="http://www.spotify.com/"

LICENSE="Spotify"
SLOT="0"
KEYWORDS="~amd64 ~x86"
IUSE="gnome"

MY_PV="${PV}.gb871a7d-1"
MY_P="${PN}-client-qt_${MY_PV}"

SRC_BASE="http://repository.spotify.com/pool/non-free/${PN:0:1}/${PN}/"
SRC_URI="
    x86?   ( ${SRC_BASE}${MY_P}_i386.deb )
    amd64? ( ${SRC_BASE}${MY_P}_amd64.deb )
"

RDEPEND="
    gnome? ( >=gnome-base/gconf-2.12 )
    >=sys-libs/glibc-2.6
    >=media-libs/alsa-lib-1.0.14
    >=x11-libs/qt-core-4.5
    >=x11-libs/qt-dbus-4.5
    >=x11-libs/qt-gui-4.5
    >=x11-libs/qt-webkit-4.5
    >=sys-devel/gcc-4.0
    sys-apps/usbutils
    "

RESTRICT="mirror strip"

src_unpack() {
    for MY_A in ${A}; do
        unpack ${MY_A}
        unpack ./data.tar.gz
    done
}

src_install() {
    mv "${WORKDIR}"/usr "${D}" || die "Install failed"
}
Update: Remove gnome-support since Spotify states this is broken and will be removed in the future.
Update2: This ebuild does NOT work on Gentoo (~amd64) right now since there is a dependency on libssl-0.9.8, and we are currently using v1.0. Hopefully this will be solved somehow. I'll remove this update when it is.

Friday, February 11, 2011

Locked svn repository and you can't unlock or clean?

I discovered that I couldn't update my subversion repository, nor could I commit, revert or even run cleanup on it! The error I got was
"Working copy is locked, run cleanup on it to remove locks"
I was wondering what the hell had happened since I hadn't locked any files on purpose. 'svn unlock --force' didn't do the job so I had to try cleanup. But when running cleanup all I got was
"svn cleanup: can't find .svn/tmp/log. "
It seems to stem from some version problem, and all that is really needed is to create a .svn/tmp directory by hand. By running a little command on the shell, we fix the problem within seconds.

find . -iname '.svn' -exec mkdir {}/tmp \;

After creating all the '/tmp' dirs, just run 'svn cleanup' again, and it should be ok for an update or a commit.

Thanks to Kyle Cordes for the fix.

Tuesday, February 8, 2011

Speeding up your secure file-transfer by using tar and ssh

Normally when transferring files between computers, one would use 'scp', and for a directory 'scp -cr'. The problem with this is that scp creates a connection for each file when transferring it, and this adds a lot of overhead.

To speed up the transfer of many small files, it is better to use the beloved Unix pipe, together with tar (the archiving software) and SSH (Secure SHell). What we do is 'tarball' all these files and directories, compress them if we would like to, and pipe the result to ssh, which on the other computer either puts them in a (compressed) tarball or untar it back into its original directory structure.

To decrease the use of bandwidth, we also set tar to filter the output through gzip, a compression program. This is done using the '-z' flag. The other two flags we use is '-c' and '-f' which means 'create archive' and which file to create (in our case '-' which means stdout) respectively.
Tar usually uses stdout if nothing else is specified, so we could actually skip the last flag.

Using scp, it would look something like this:
scp -rc directory/ user@host:~/

But with tar and ssh, we instead write it like:
tar -zcf - directory/ | ssh user@host "tar zxvf -"

I have created a directory on the receiving host called 'backup', so I want to put it into that directory instead:
tar -zcf - directory/ | ssh user@host "tar zxvf - -C backup/"

or if we remove the '-f' flag with its corresponding argument:
tar -zc directory/ | ssh user@host "tar zxv -C backup/"

I tried using bzip2 as a filter as well, but it seems to have a tendency to be too cpu-heavy in comparison to its improved compression ratio. But this depends on the amount of available bandwidth.

I found some comparisons at http://smaftoul.wordpress.com/2008/08/19/ssh-scp-little-files-and-tar/, which saw an 19x transfer time improvement when transferring a 109 Mb large directory containing 9992 files. This is definitely worth a little command line magic!

Thursday, January 6, 2011

Native Spotify ebuild for Gentoo

You also love Spotify? Well, it truly is the best thing since the Interwebs, that's for sure!

A lot of Linux users don't know that the wonderful developers over at Spotify are actually releasing early beta builds of their native Linux client. It's not perfect, but for being early-releases it's damn near close! You just have to love those guys...

Anyway, here's an ebuild I've completely stolen from dstien a couple of months ago, and then updated to new versions as they've come out. I've added the Pulseaudio dependency (dstien doesn't seen to have done that yet) since the previous version didn't work without it).

Ebuild:
# Copyright 1999-2010 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $

EAPI="2"

DESCRIPTION="Spotify desktop client"
HOMEPAGE="http://www.spotify.com/"

LICENSE="Spotify"
SLOT="0"
KEYWORDS="~amd64 ~x86"
IUSE="gnome"

MY_PV="${PV}.g604b4fb-1"
MY_P="${PN}-client-qt_${MY_PV}"

SRC_BASE="http://repository.spotify.com/pool/non-free/${PN:0:1}/${PN}/"
SRC_URI="
    x86?   ( ${SRC_BASE}${MY_P}_i386.deb )
    amd64? ( ${SRC_BASE}${MY_P}_amd64.deb )
    gnome? ( ${SRC_BASE}${PN}-client-gnome-support_${MY_PV}_all.deb )
    "

RDEPEND="
    >=media-libs/alsa-lib-1.0.14
    >=media-sound/pulseaudio-0.9.21
    >=sys-devel/gcc-4.0
    >=sys-libs/glibc-2.6
    >=x11-libs/qt-core-4.5
    >=x11-libs/qt-dbus-4.5
    >=x11-libs/qt-gui-4.5
    >=x11-libs/qt-webkit-4.5
    "

RESTRICT="mirror strip"

src_unpack() {
    for MY_A in ${A}; do
        unpack ${MY_A}
        unpack ./data.tar.gz
    done
}

src_install() {
    mv "${WORKDIR}"/usr "${D}" || die "Install failed"
}
Just copy this into an empty file within your portage overlay (for example /usr/local/portage/) in the media-sound directary, go into the directory and run

> ebuild spotify-0.4.9.302.ebuild digest

and then you're ready to go! Should be at least ;)

Saturday, August 21, 2010

Logging boot output

Ever seen an unnerving [ !! ] scroll by during boot, hidden among 50 lines, way too fast to be able to actually read the problem? Then X then starts, it clears the buffer in the TTY, not allowing you to scroll up to read it when you do a VT-switch back to TTY1.

While 'dmesg' (or reading /var/log/dmesg or /var/log/messages) shows you the boot output from the kernel and other parts of the system, it usually doesn't show the issues from specific boot processes that show up in the list with [ ok ] or [ !! ]. Well, we can actually enable the logging of the console output from the boot process by a few simple steps.

This applies to a Gentoo system, and I'm not sure which other distributions it works for.

#1. First enable RC logging by editing '/etc/rc.conf'. Uncomment the following line (marked by '>'):
# rc_logger launches a logging daemon to log the entire rc process to
# /var/log/rc.log
# NOTE: Linux systems require the devfs service to be started before
# logging can take place and as such cannot log the sysinit runlevel.> rc_logger="YES"

Then we also need to install the showconsole daemon which does the actual logging.

> eix-sync showconsole
[I] app-admin/showconsole
     Available versions:  1.07 1.08
     Installed versions:  1.08(22.23.03 2010-08-21)
     Homepage:            http://www.novell.com/linux/suse/
     Description:         small daemon for logging console output during boot
Just run 'emerge showconsole' and reboot, after that all the messages scrolling past the screen as you boot will be stored in /var/log/rc.log.

Tuesday, May 11, 2010

Using the command line to find out your router's external IP address

Ever been in the position where you need to find out your IP address without having a GUI to fall back on and surf to www.whatsmyip.org or the likes?

At tips4Linux I just found a great command for it! Just fire up wget and get the address printed right there in your terminal.

wget -O - -q icanhazip.com

That's it!