#!/usr/bin/perl

#
# NMSTL, the Networking, Messaging, Servers, and Threading Library for C++
# Copyright (c) 2002 Massachusetts Institute of Technology
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

#
# The idea for much of this code came from libasync, by Dave Mazieres
# (in fact, NMSTL used to use a gencallback.pm derived from libasync
# code).  This implementation was written independently, however.
#

use strict;

my $maxbound = 3;
my $maxfree = 3;

sub dosub1 {
    my $begintmpl = shift;
    my $comma = shift;
    my $arg = shift;
    my $endtmpl = shift;
    my $map = shift;

    my $keys = join("", keys %$map);

    my $maybe = $endtmpl =~ s/^\!//;

    if ($arg =~ /([$keys])1/i) {
        my $which = $1;
        my $cnt = $map->{uc $which};

        my $default = "";
        $arg =~ s{ /(\w+) $ }{ $default = $1; "" }ex;

        return "$begintmpl$default$endtmpl" if ($cnt == 0);

        if ($maybe && defined($map->{"max$which"}) && $cnt == $map->{"max$which"}) {
            return "";
        }

        my $joinstr = $arg =~ /;$/ ? " " : ", ";

        return "$begintmpl$comma" .
            join($joinstr, map { my $t = $arg; $t =~ s/($which) 1/$1$_/ixg; $t } (1..$cnt)) .
            "$endtmpl";

    }

    die "No substitution for $keys in:\n$arg\n";
}

sub dosub {
    my $arg = shift;
    my $map = shift;
    $arg =~ s{
                 (<\w* | template<)?
                 (,\s*)?
                 \[~(.+?)~\]
                 (!?>)?
             }{ dosub1($1, $2, $3, $4, $map) }egx;

    $arg =~ s{( [(<] ) ,\s*}{$1}gx;  # Turn "foo(, a" into "foo(a"
    $arg =~ s{\s*, ( [)>] )}{$1}gx;  # Turn "b, )" into "b)"

    $arg =~ s{template<\?>}{}g;
    $arg =~ s{<\?>}{}g;
    $arg =~ s{\?>}{>}g;

    return $arg;
}

sub printsub {
    print dosub(@_);
}

printsub <<EOF, { F => $maxfree };
/*
 * NMSTL, the Networking, Messaging, Servers, and Threading Library for C++
 * Copyright (c) 2002 Massachusetts Institute of Technology
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef CALLBACK_H
#define CALLBACK_H

#include <set>
#include <nmstl/ptr>

#ifndef DOXYGEN_SKIP
NMSTL_NAMESPACE_BEGIN;

template<typename R, [~typename F1 = void~]> class callback;
template<[~typename F1 = void~]> class listeners;

template<typename R, [~typename F1~]>
inline string to_string(const callback<R, [~F1~]>& cb) {
    return cb.as_string();
}
EOF

foreach my $free (0..$maxfree) {
    my $friends = "";

    printsub <<EOF, { F => $free, maxF => $maxfree };

template<typename R, [~typename F1~]>
class callback<R, [~F1~]!> {
  public:
    struct func {
        typedef ptr<func> ptr;
        virtual R operator () ([~F1~]) = 0;
        virtual ~func() {}
    };
    typedef ptr<func> fptr;

  private:
    mutable fptr fn;

  public:
    callback() {}
    callback(fptr fn) : fn(fn) {}
    callback(func* fn) : fn(fn) {}
    R operator() ([~F1 f1~]) const { ASSERT(fn); return (*fn)([~f1~]); }
    string as_string() const { return "callback{" + fn.debug_repr() + "}"; }
EOF

    foreach my $bound (0..$maxbound) {
        printsub <<EOF, { F => $free, B => $bound };

    template<[~typename B1~]?>
    struct pfunc_${free}_${bound} : public func {
        typedef R(*fp_t)([~B1~], [~F1~]); fp_t fp; [~B1 b1;~]
        pfunc_${free}_${bound}(fp_t fp, [~B1 b1~]) : fp(fp), [~b1(b1)~] { ASSERT(fp); }
        R operator() ([~F1 f1~]) { return fp([~b1~], [~f1~]); }
    };

    template<[~typename B1, typename B1x~]?>
    static func* init( R(*fp)([~B1~], [~F1~]), [~B1x b1~] ) {
        return new pfunc_${free}_${bound}<[~B1~]?>(fp, [~b1~]);
    }

    template<typename O, [~typename B1~]>
    struct ofunc_${free}_${bound} : public func {
	O& o;
	typedef R(O::*fp_t)([~B1~], [~F1~]); fp_t fp; [~B1 b1;~]
        ofunc_${free}_${bound}(O& o, fp_t fp, [~B1 b1~]) : o(o), fp(fp), [~b1(b1)~] { ASSERT(&o); ASSERT(fp); }
        R operator() ([~F1 f1~]) { return (o.*fp)([~b1~], [~f1~]); }
    };

    template<typename O, [~typename B1, typename B1x~]>
    static func* init( O& o, R(O::*fp)([~B1~], [~F1~]), [~B1x b1~] ) {
        return new ofunc_${free}_${bound}<O, [~B1~]>(o, fp, [~b1~]);
    }

    template<[~typename B1, typename B1x~]?>
    callback( R(*fp)([~B1~], [~F1~]), [~B1x b1~] ) : fn( init(fp, [~b1~]) ) {}
    template<typename O, [~typename B1, typename B1x~]>
    callback( O* o, R(O::*fp)([~B1~], [~F1~]), [~B1x b1~] ) : fn( init(*o, fp, [~b1~]) ) {}
    template<typename O, [~typename B1, typename B1x~]>
    callback( O& o, R(O::*fp)([~B1~], [~F1~]), [~B1x b1~] ) : fn( init(o, fp, [~b1~]) ) {}

EOF

    $friends .= dosub(<<EOF, { F => $free, B => $bound });

template<typename R, [~typename F1~], [~typename B1, typename B1x~]>
inline callback<R, [~F1~]> wrap( R(*fp)([~B1~], [~F1~]), [~B1x b1~] ) {
    return callback<R, [~F1~]>::init(fp, [~b1~]);
}
template<typename O, typename R, [~typename F1~], [~typename B1, typename B1x~]>
inline callback<R, [~F1~]> wrap( O* o, R(O::*fp)([~B1~], [~F1~]), [~B1x b1~] ) {
    return callback<R, [~F1~]>::init(*o, fp, [~b1~]);
}
template<typename O, typename R, [~typename F1~], [~typename B1, typename B1x~]>
inline callback<R, [~F1~]> wrap( O& o, R(O::*fp)([~B1~], [~F1~]), [~B1x b1~] ) {
    return callback<R, [~F1~]>::init(o, fp, [~b1~]);
}

EOF

    }

    printsub <<EOF, { F => $free, maxF => $maxfree };

    bool operator == (const callback<R, [~F1~]>& other) const { return fn == other.fn; }
    bool operator < (const callback<R, [~F1~]>& other) const { return fn < other.fn; }
    operator const void *() const { return fn; }
};

$friends

template<[~typename F1~]>
class listeners<[~F1~]!> {
    typedef callback<void, [~F1~]> CB;

    set<CB> cbs;

  public:
    void add(CB cb) { cbs.insert(cb); }
    void remove(CB cb) { cbs.erase(cb); }
    void post([~F1 f1~]) {
        for (@{[ $free ? "typename " : ""]}set<CB>::const_iterator i = cbs.begin(); i != cbs.end(); ++i)
            (*i)([~f1~]);
    }
    void swap(listeners<[~F1~]>& other) {
        cbs.swap(other.cbs);
    }
};

EOF
}




print <<EOF;

NMSTL_NAMESPACE_END;

#else // def DOXYGEN_SKIP

/**
 * Generic callbacks.
 *
 * A callback<R, T1, T2...> is a function that you can call with
 * arguments T1, T2, ... and obtain a result of type R.  (R may
 * be void, and there can be zero or more arguments.)  For instance:
 *
 * <ul>
 * <li>callback<void> is a function which takes no arguments and returns no result.
 * <li>callback<string, int> is a function which takes an int as a input and returns a string.
 * <li>callback<string, int, vector<int> > is a function which takes an int and a vector<int>
 * as input and returns a string.
 * </ul>
 *
 * You can invoke a callback just as you'd invoke a function:
 *
 * \code
 * callback<string, int, bool> my_callback = ...;
 * string s = my_callback(3, true);
 * \endcode
 *
 * To create a callback on a function, pass a pointer to the
 * function as the first
 * argument to the callback constructor:
 *
 * \code
 * string my_function(int a, bool b);
 * callback<string, int, bool> my_callback(&my_function);
 *   ...
 *
 * // later
 * string s = my_callback(3, true);
 * \endcode
 *
 * You can also "bind" arguments to your function.  They are stored
 * in the callback object and are passed to your function when it is
 * invoked later:
 *
 * \code
 * string my_function(int a, bool b);
 * callback<string, bool> my_callback(&my_function, 3);
 * // N.B.: no "int" in type, since we've already specified the int
 * // in the callback constructor!
 *   ...
 * 
 * // later
 * string s = my_callback(true);
 * \endcode
 *
 * You may also create callbacks from method pointers to objects; just pass a
 * pointer or reference to the object as the first argument to the constructor, and the method
 * pointer second.  (Note that create a method pointer in ISO C++, you
 * must use the syntax &ClassName::method_name; compilers are not
 * supposed to let you simply say &method_name within the class.)
 *
 * \code
 * class MyFooClass {
 *     int some_instance_variable;
 *
 *   public:
 *     string bar(int a, bool b);
 * };
 *
 * MyFooClass &foo;
 * callback<string, int, bool> my_callback_1(foo, &MyFooClass::bar);
 * callback<string, bool> my_callback(foo, &MyFooClass::bar, 3);
 *   ...
 *
 * // later
 * string s = my_callback(3, true);
 * string t = my_callback(true);
 * \endcode
 *
 * Callbacks may be passed by value.  As the name "callback" suggests,
 * one generally creates a callback and passes it to other code, which
 * then invokes the callback at some later point.  For instance:
 *
 * class my_thread : public thread {
 *   public:
 *     callback<bool, int> notifier;
 *
 *     void run() {
 *         while (1) {
 *             int result;
 *
 *             // perform some lengthy operation
 *               ...
 *
 *         // Notify the caller that we're done, and what the
 *         // result was.
 *         bool happy = notifier(result);
 *
 *         if (happy)
 *         
 *     
 *
 **/
template<class R, class T1, class T2, class etc>
class callback {
    R operator() (T1 t1, T2 t2, etc...) const;
};

#endif // ndef DOXYGEN_SKIP
#endif // ndef CALLBACK_H



EOF
