view qt/d2/qt/Signal.d @ 288:f9559a957be9 signals

new signals and slots implementation
author eldar
date Sun, 08 Nov 2009 19:28:01 +0000
parents 1f6923c8cba0
children c9d1aac290e9
line wrap: on
line source

/**
 *
 *  Copyright: Copyright QtD Team, 2008-2009
 *  Authors: Max Samukha, Eldar Insafutdinov
 *  License: <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a>
 *
 *  Copyright QtD Team, 2008-2009
 *  Distributed under the Boost Software License, Version 1.0.
 *  (See accompanying file boost-license-1.0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 *
 */
module qt.Signal;

public import qt.QGlobal;
import qt.qtd.MetaMarshall;

import core.stdc.stdlib : crealloc = realloc, cfree = free;
import core.stdc.string : memmove;
import
    core.thread,
    core.exception,
    std.algorithm;

public import
    std.typetuple,
    std.traits,
    std.conv,
    std.string,
    std.metastrings;

   
// returns name, arguments or tuple of the function depending on type parameter
enum {_Name, _Tuple, _Args}
string getFunc(int type)(string fullName)
{
    int pos = 0;
    foreach(i, c; fullName)
        if (c == '(')
            static if (type == _Tuple)
                return fullName[i..$];
            else if (type == _Name)
                return fullName[0..i];
            else if (type == _Args)
                for(int j = fullName.length-1;; j--)
                    if(fullName[j] == ')')
                        return fullName[i+1 .. j];
    return null;
}

/** The beast that takes string representation of function arguments
  * and returns an array of default values it doesn't check if arguments
  * without default values follow the arguments with default values for
  * simplicity. It is done by mixing in an delegate alias.
  */
string[] defaultValues(string signature)
{
    int braces = 0;
    bool inDefaultValue = false;
    bool inStringLiteral = false;
    string[] res;
    int startValue = 0;
    
    if(strip(signature).length == 0)
        return res;

    foreach (i,c; signature)
    {
        if(!inStringLiteral)
        {
            if(c == '{' || c =='(')
                braces++;
            else if(c == '}' || c ==')')
                braces--;
        }

        if(c == '\"' || c == '\'')
        {
            if (inStringLiteral)
            {
                if(signature[i-1] != '\\')
                    inStringLiteral = false;
            }
            else
            {
                inStringLiteral = true;
            }
        }
        
        if (!inStringLiteral && braces == 0)
        {
            if(c == '=') // found default value
            {
                inDefaultValue = true;
                startValue = i+1;
            }
            else if(c == ',') // next function argument
            {
                if (inDefaultValue)
                {
                    res ~= signature[startValue..i];
                    inDefaultValue = false;
                }
            }
        }
    }
    
    if (inDefaultValue)
        res ~= signature[startValue..$];

    return res;
}

int defaultValuesLength(string[] defVals)
{
    return defVals.length;
}

/**
    New implementation.
*/


// need this to mark static metamethods-info whether it's generated by presence of default args
enum DefaultArgs
{
    None, Start, Continue
}

// templates for extracting data from static meta-information of signals, slots or properties
// public alias TypeTuple!("name", index, OwnerClass, DefaultArgs.Start, ArgTypes) __signal
template MetaEntryName(source...)
{
    enum MetaEntryName = source[0]; // name of the metaentry is the first element
}

template MetaEntryOwner(source...)
{
    alias TupleWrapper!(source[2]).at[0] MetaEntryOwner; // class that owns the property is the third
    // Compiler #BUG 3092 - evaluates MetaEntryOwner as a Tuple with one element
}

template MetaEntryArgs(source...)
{
    alias source[4 .. $] MetaEntryArgs; // arguments-tuple starts from the fourth position
}

template TupleWrapper(A...) { alias A at; }

template isDg(Dg)
{
    enum isDg = is(Dg == delegate);
}

template isFn(Fn)
{
    enum isFn = is(typeof(*Fn.init) == function);
}

template isFnOrDg(Dg)
{
    enum isFnOrDg = isFn!(Dg) || isDg!(Dg);
}

string joinArgs(A...)()
{
    string res = "";
    static if(A.length)
    {
        res = A[0].stringof;
        foreach(k; A[1..$])
            res ~= "," ~ k.stringof;
    }
    return res;
}

template SlotPred(T1, T2)
{
    enum SlotPred = is(T1 : T2);
}

template CheckSlot(alias Needle, alias Source)
{
    static if(Needle.at.length <= Source.at.length)
        enum CheckSlot = CheckArgs!(Needle, Source, SlotPred, 0).value;
    else
        enum CheckSlot = false;
}

template SignalPred(T1, T2)
{
    enum SignalPred = is(T1 == T2);
}

template CheckSignal(alias Needle, alias Source)
{
    static if(Needle.at.length == Source.at.length)
        enum CheckSignal = CheckArgs!(Needle, Source, SignalPred, 0).value;
    else
        enum CheckSignal = false;
}

template CheckArgs(alias Needle, alias Source, alias pred, int i)
{
    static if (i < Needle.at.length)
    {
        static if (pred!(Needle.at[i], Source.at[i]))
            enum value = CheckArgs!(Needle, Source, pred, i + 1).value;
        else
            enum value = false;
    }
    else
    {
        enum value = true;
    }
}

template SigByNamePred(string name, SlotArgs...)
{
    template SigByNamePred(source...)
    {
        static if (source[0] == name) // only instantiate CheckSlot if names match
            enum SigByNamePred = CheckSlot!(TupleWrapper!(SlotArgs), TupleWrapper!(source[2 .. $]));
        else
            enum SigByNamePred = false;
    }
}

template SigBySignPred(string name, SigArgs...)
{
    template SigBySignPred(source...)
    {
        static if (source[0] == name) // only instantiate CheckSignal if names match
            enum SigBySignPred = CheckSignal!(TupleWrapper!(SigArgs), TupleWrapper!(source[2 .. $]));
        else
            enum SigBySignPred = false;
    }
}

template ByOwner(Owner)
{
    template ByOwner(source...)
    {
        enum ByOwner = is(MetaEntryOwner!source == Owner);
    }
}

template staticSymbolName(string prefix, int id)
{
    const string staticSymbolName = prefix ~ ToString!(id);
}

template signatureString(string name, A...)
{
    const string signatureString = name ~ "(" ~ joinArgs!(A) ~ ")";
}

// recursive search in the static meta-information
template findSymbolImpl(string prefix, C, int id, alias pred)
{
    static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) )
    {
        mixin ("alias C." ~ staticSymbolName!(prefix, id) ~ " current;");
        static if (pred!current)
            alias current result;
        else
            alias findSymbolImpl!(prefix, C, id + 1, pred).result result;
    }
    else
    {
        alias void result;
    }
}

template findSymbol(string prefix, C, alias pred)
{
    alias findSymbolImpl!(prefix, C, 0, pred).result findSymbol;
}

template findSignal(C, string name, Receiver, SigArgs...)
{
    alias TupleWrapper!(ParameterTypeTuple!Receiver) SlotArgsWr;
    static if (SigArgs.length > 0)
    {
        alias findSymbol!(signalPrefix, C, SigBySignPred!(name, SigArgs)) result;
        static if (is(result == void))
            static assert(0, "Signal " ~ name ~ "(" ~ joinArgs!SigArgs() ~ ") was not found.");
        else
            static if (!CheckSlot!(SlotArgsWr, TupleWrapper!(result[2 .. $])))
                static assert(0, "Signature of slot is incompatible with signal " ~ name ~ ".");
    }
    else
    {
        alias findSymbol!(signalPrefix, C, SigByNamePred!(name, SlotArgsWr.at)) result;
        static if (is(result == void))
            static assert(0, "Signal " ~ name ~ " was not found.");
    }
}

// recursive search in the static meta-information
template findSymbolsImpl(string prefix, C, int id, alias pred)
{
    static if ( is(typeof(mixin("C." ~ staticSymbolName!(prefix, id)))) )
    {
        mixin ("alias C." ~ staticSymbolName!(prefix, id) ~ " current;");
        static if (pred!current) {
            alias TupleWrapper!current subres;
//                    pragma(msg, toStringNow!id ~ " " ~ subres.stringof);
        } else
            alias TypeTuple!() subres;
        alias TypeTuple!(subres, findSymbolsImpl!(prefix, C, id + 1, pred).result) result;
    }
    else
    {
        alias TypeTuple!() result;
    }
}

template findSymbols(string prefix, C, alias pred)
{
    alias findSymbolsImpl!(prefix, C, 0, pred).result findSymbols;
}

string __toString(long v)
{
    if (v == 0)
        return "0";

    string ret;

    bool neg;
    if (v < 0)
    {
        neg = true;
        v = -v;
    }

    while (v != 0)
    {
        ret = cast(char)(v % 10 + '0') ~ ret;
        v = cast(long)(v / 10);
    }

    if (neg)
        ret = "-" ~ ret;

    return ret;
}

string convertSignalArguments(Args...)()
{
//        void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };

    string res = "void*[" ~ __toString(Args.length+1) ~ "] _a = [null";
    foreach(i, _; Args)
        res ~= ", " ~ "cast(void*) &" ~ convertSignalArgument!(Args[i])("_t" ~ __toString(i));
    res ~= "];\n";
    return res;
}

public string SignalEmitter(A...)(SignalType signalType, string name, string[] defVals, int localIndex)
{
    string fullArgs, args;
    int defValsLength = defVals.length;
    string argsConversion = "";
    string argsPtr = "null";
    static if (A.length)
    {
        while(A.length != defVals.length)
            defVals = "" ~ defVals;
        
        fullArgs = A[0].stringof ~ " _t0";
        if (defVals[0].length)
            fullArgs ~= " = " ~ defVals[0];
        args = "_t0";
        foreach(i, _; A[1..$])
        {
            fullArgs ~= ", " ~ A[i+1].stringof ~ " _t" ~ __toString(i+1);
            if (defVals[i+1].length)
                fullArgs ~= " = " ~ defVals[i+1];
            args ~= ", _t" ~ __toString(i+1);
        }
        // build up conversion of signal args from D to C++
        argsPtr = "_a.ptr";
        argsConversion = convertSignalArguments!(A)();
    }
    string attribute;
    string sigName = name;
    if (signalType == SignalType.BindQtSignal)
        name ~= "_emit";
    else
        attribute = "protected ";
    
    string indexArgs = __toString(localIndex);
    if(defValsLength > 0)
        indexArgs ~= ", " ~ __toString(localIndex+defValsLength);
    string str = attribute ~ "final void " ~ name ~ "(" ~ fullArgs ~ ") {\n" ~ argsConversion ~ "\n"
                           ~ "    QMetaObject.activate(this, typeof(this).staticMetaObject, " ~ indexArgs ~ ", " ~ argsPtr ~ ");\n"
                           ~ "}\n"; // ~
    return str;
}
/** ---------------- */


const string signalPrefix = "__signal";
const string slotPrefix = "__slot";

enum SignalType
{
    BindQtSignal,
    NewSignal,
    NewSlot
}

template BindQtSignal(string fullName)
{
    mixin MetaMethodImpl!(signalPrefix, 0, fullName, SignalType.BindQtSignal);
}

template Signal(string fullName)
{
    mixin MetaMethodImpl!(signalPrefix, 0, fullName, SignalType.NewSignal);
}

template Slot(string fullName)
{
    mixin MetaMethodImpl!(slotPrefix, 0, fullName, SignalType.NewSlot);
}

template SignalImpl(int index, string fullName, SignalType signalType)
{
    static if (is(typeof(mixin(typeof(this).stringof ~ "." ~ signalPrefix ~ ToString!(index)))))
        mixin SignalImpl!(index + 1, fullName, signalType);
    else
    {
//        pragma(msg, "alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;");
        mixin("alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;");
        alias ParameterTypeTuple!(Dg) ArgTypes;
        enum args = getFunc!_Args(fullName);
        enum defVals = defaultValues(args);
        enum defValsLength = defaultValuesLength(defVals);

//        pragma (msg, SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, index));
        mixin InsertMetaSignal!(fullName, index, defValsLength, ArgTypes);
//        pragma (msg, ctfe_meta_signal!(ArgTypes)(fullName, index, defValsLength));
    }
}
template MetaMethodImpl(string metaPrefix, int index, string fullName, SignalType signalType)
{
    static if (is(typeof(mixin(typeof(this).stringof ~ "." ~ metaPrefix ~ toStringNow!(index)))))
    {
        mixin MetaMethodImpl!(metaPrefix, index + 1, fullName, signalType);
    }
    else
    {
        mixin("alias void delegate" ~ getFunc!_Tuple(fullName) ~ " Dg;");
        alias ParameterTypeTuple!(Dg) ArgTypes;
        enum args = getFunc!_Args(fullName);
        enum defVals = defaultValues(args);
        enum defValsLength = defaultValuesLength(defVals);
        
        static if (metaPrefix == signalPrefix)
        {
            // calculating local index of the signal
            static if (typeof(this).stringof == "QObject")
                enum localIndex = index;
            else
                mixin ("enum localIndex = index - 1 - lastSignalIndex_" ~ (typeof(super)).stringof ~ ";");
            
            static if (signalType == SignalType.NewSignal)
            {
                pragma (msg, SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, localIndex));
                mixin (SignalEmitter!(ArgTypes)(SignalType.NewSignal, getFunc!_Name(fullName), defVals, localIndex));
            }
        }
        mixin InsertMetaMethod!(fullName, metaPrefix, index, defValsLength, DefaultArgs.Start, ArgTypes);
//        pragma (msg, ctfe_meta_signal!(ArgTypes)(fullName, index, defValsLength));
    }
}
template InsertMetaMethod(string fullName, string metaPrefix, int index, int defValsCount, DefaultArgs defArgsInitFlag, ArgTypes...)
{
    // this identifies if metamethod is was generated by the presence of default args or not
    static if(defValsCount > 0)
    {
        static if (defArgsInitFlag == DefaultArgs.Start)
            enum defValsFlag = DefaultArgs.Start;
        else
            enum defValsFlag = DefaultArgs.Continue;
    }
    else
    {
        static if (defArgsInitFlag == DefaultArgs.Start)
            enum defValsFlag = DefaultArgs.None;
        else
            enum defValsFlag = DefaultArgs.Continue;
    }
    static if(defValsCount >= 0)
        mixin("public alias TypeTuple!(\"" ~ getFunc!_Name(fullName) ~ "\", index, typeof(this), defValsFlag, ArgTypes) " ~ metaPrefix ~ toStringNow!(index) ~ ";");
    static if(defValsCount > 0)
        mixin InsertMetaMethod!(fullName, metaPrefix, index+1, defValsCount-1, DefaultArgs.Continue, ArgTypes[0..$-1]);
}


string signature_impl(T...)(string name)
{
    string res = name ~ "(";
    foreach(i, _; T)
    {
        if(i > 0)
            res ~= ",";
        res ~= T[i].stringof;
    }
    res ~= ")";
    return res;
}

template signature(string name, T...)
{
    enum signature = signature_impl!(T)(name);
}

template lastSignalIndex(T)
{
    static if (T.stringof == "QObject")
        enum lastSignalIndex = lastSignalIndexImpl!(T, 0);
    else
        mixin ("enum lastSignalIndex = lastSignalIndexImpl!(T, " ~ "T.lastSignalIndex_" ~ (BaseClassesTuple!(T)[0]).stringof ~ ");");
}

template lastSignalIndexImpl(T, int index)
{
    static if (is(typeof(mixin("T." ~ signalPrefix ~ toStringNow!(index)))))
        enum lastSignalIndexImpl = lastSignalIndexImpl!(T, index + 1);
    else
        enum lastSignalIndexImpl = index - 1;
}