SORU
28 Kasım 2010, Pazar


5 yıl sonra, orada bir şey " daha;Mümkün olan en Hızlı C Delegeler"?

"C delegeler" ölüm için yapıldı, ve http://www.codeproject.com http://stackoverflow.com hem de derin bir soruyu kapsamaktadır. konu biliyorum

Genellikle, Don Clugston's fastest possible delegate birçok kişi için ilk seçenek gibi görünüyor. Birkaç diğer popüler olanları vardır.

Ancak, bu makalelerin çoğu eski olduğunu fark ettim (2005) ve birçok tasarım seçenekleri hesaba VC7 gibi eski Derleyiciler alarak yapılmış gibi.

Bir ses uygulaması için çok hızlı bir temsilci uygulamaya ihtiyacım var.

Ben hala ama ben sadece modern Derleyiciler (VC9, VS2008 SP1 ve GCC 4.5.kullanımı taşınabilir olması gerekir (Windows, Mac, Linux) x).

Ana kriterim:

  • hızlı olmalı!
  • ileriye dönük uyumlu uygulamaları yeni sürümleri ile olmalı. O açıkça standart uyumlu değil states çünkü Don'un uygulanması ile ilgili bazı şüphelerim var.
  • isteğe bağlı olarak, bir sözdizimi ÖPÜCÜK ve kullanım kolaylığı güzel
  • çok noktaya çok kolay herhangi bir temsilci kütüphane etrafında inşa ikna olsam da güzel olurdu

Ayrıca, gerçekten egzotik özelliklere ihtiyacım yok. Ben sadece işaretçi yöntemi eski güzel şeye ihtiyacım var. Hayır böyle statik yöntemler, ücretsiz işlevleri veya şeyleri desteklemek lazım.

Bugün itibariyle, önerilen yaklaşım nedir? Hala Don's version kullan? Veya orada bir "uzlaşma başka bir seçenek" topluluktur

Gerçekten Boost kullanmak istemiyorum.performans açısından kabul edilebilir bir şey değil çünkü sinyal/signal2. QT bir bağımlılık kabul edilebilir gibi değil.

Ayrıca, googling bazı yeni kütüphaneler gördüm, örneğin cpp-events ama kullanıcılardan gelen herhangi bir geri bildirim, vb dahil olmak üzere bulamadım.

CEVAP
29 Kasım 2010, PAZARTESİ


Güncelleme:An article with the complete source code and a more detailed discussion has been posted on The Code Project.

Peki, yöntemleri işaretçiler ile sorun hepsi aynı boyutta değiller. Yöntemleri işaretçiler doğrudan saklamak yerine, "sabit bir boyutu bu." onları standardize etmek lazım yani Bu Don Clugston Kodu Proje yazısında ulaşmak için çalışır. En popüler uygulamaları hakkında ayrıntılı bir bilgi kullanarak yapıyor. Ben bunu iddia bunu yapmak için mümkün olduğunca "normal" C böyle bir bilgiye ihtiyaç olmadan.

Aşağıdaki kodu göz önünde bulundurun:

void DoSomething(int)
{
}

void InvokeCallback(void (*callback)(int))
{
    callback(42);
}

int main()
{
    InvokeCallback(&DoSomething);
    return 0;
}

Bu bir geri arama fonksiyonu düz eski bir işaretçi kullanarak uygulamak için bir yoldur. Ancak, bu nesnelere yöntem işe yaramıyor. Hadi bunu düzeltme:

class Foo
{
public:
    void DoSomething(int) {}

    static void DoSomethingWrapper(void* obj, int param)
    {
        static_cast<Foo*>(obj)->DoSomething(param);
    }
};

void InvokeCallback(void* instance, void (*callback)(void*, int))
{
    callback(instance, 42);
}

int main()
{
    Foo f;
    InvokeCallback(static_cast<void*>(&f), &Foo::DoSomethingWrapper);
    return 0;
}

Şimdi, her ikisi de ücretsiz ve üye fonksiyonları için çalışabilir geri bir sistem var. Bu, ancak, hantal ve hata eğilimli. Ancak, bir kalıp - "statik işlevi uygun bir örnek. bir yöntem aramak için" ileri kapsayıcı bir işlevi var Bu şablonlar ile yardımcı olmak için kullanın. sarıcı işlevi genelleme deneyelim:

template<typename R, class T, typename A1, R (T::*Func)(A1)>
R Wrapper(void* o, A1 a1)
{
    return (static_cast<T*>(o)->*Func)(a1);

}

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(void* instance, void (*callback)(void*, int))
{
    callback(instance, 42);
}

int main()
{
    Foo f;
    InvokeCallback(static_cast<void*>(&f),
        &Wrapper<void, Foo, int, &Foo::DoSomething> );
    return 0;
}

Bu hala son derece beceriksiz, ama en azından artık kapsayıcı bir işlev her zaman (en az 1 değişken durum için) yazmak zorunda değiliz. Genelleme yapabileceğimiz başka bir şey her zaman void* bir işaretçi geçiyoruz aslında. İki farklı değerler geçirmeden yerine, neden onları bir araya koymak değil mi? Ve biz bunu yaparken, neden genelleme değil de? Hadi bir fonksiyon gibi bir şey de diyebiliriz yani operator()() atmak!

template<typename R, typename A1>
class Callback
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback(void* o, FuncType f) : obj(o), func(f) {}
    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, class T, typename A1, R (T::*Func)(A1)>
R Wrapper(void* o, A1 a1)
{
    return (static_cast<T*>(o)->*Func)(a1);

}

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(Callback<void, int> callback)
{
    callback(42);
}

int main()
{
    Foo f;
    Callback<void, int> cb(static_cast<void*>(&f),
        &Wrapper<void, Foo, int, &Foo::DoSomething>);
    InvokeCallback(cb);
    return 0;
}

İlerleme kaydediyoruz! Ama şimdi bizim sorunumuz sözdizimi kesinlikle korkunç olduğu bir gerçektir. Sözdizimi görünür gereksiz; derleyici yöntemi için işaretçiyi gelen tipleri yolunu kendisi olabilir mi? Ne yazık ki Hayır, ama birlikte yardım edebiliriz. Bir derleyici, bir işlev çağrısı şablon değişkeni kesintisi üzerinden türlerini anlamak gerekir. Niçin başlamıyorsun?

template<typename R, class T, typename A1>
void DeduceMemCallback(R (T::*)(A1)) {}

İşlev içinde, R, T A1 ne olduğunu biliyoruz. Eğer "" bu tür ve işlevini geri vermek mi? tutabileceği bir yapı inşa edebiliriz.

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag2<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

DeduceMemCallbackTag türlerini bilir neden bu yana statik bir fonksiyonu olarak sarıcı işlevi, sadece koyun değil mi? Ve sarıcı işlevi olduğundan, neden kodu Callback nesne oluşturmak için koymak değil mi?

template<typename R, typename A1>
class Callback
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback(void* o, FuncType f) : obj(o), func(f) {}
    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
    template<R (T::*Func)(A1)>
    static R Wrapper(void* o, A1 a1)
    {
        return (static_cast<T*>(o)->*Func)(a1);
    }

    template<R (T::*Func)(A1)>
    inline static Callback<R, A1> Bind(T* o)
    {
        return Callback<R, A1>(o, &DeduceMemCallbackTag::Wrapper<Func>);
    }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

C standardı bizi örnekleri statik işlevleri çağırmak için izin verir (!):

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(Callback<void, int> callback)
{
    callback(42);
}

int main()
{
    Foo f;
    InvokeCallback(
        DeduceMemCallback(&Foo::DoSomething)
        .Bind<&Foo::DoSomething>(&f)
    );
    return 0;
}

Yine de, uzun bir anlatım oldu, ama basit bir makro oldu (!):

template<typename R, typename A1>
class Callback
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback(void* o, FuncType f) : obj(o), func(f) {}
    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
    template<R (T::*Func)(A1)>
    static R Wrapper(void* o, A1 a1)
    {
        return (static_cast<T*>(o)->*Func)(a1);
    }

    template<R (T::*Func)(A1)>
    inline static Callback<R, A1> Bind(T* o)
    {
        return Callback<R, A1>(o, &DeduceMemCallbackTag::Wrapper<Func>);
    }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

#define BIND_MEM_CB(memFuncPtr, instancePtr) \
    (DeduceMemCallback(memFuncPtr).Bind<(memFuncPtr)>(instancePtr))

class Foo
{
public:
    void DoSomething(int) {}
};

void InvokeCallback(Callback<void, int> callback)
{
    callback(42);
}

int main()
{
    Foo f;
    InvokeCallback(BIND_MEM_CB(&Foo::DoSomething, &f));
    return 0;
}

Güvenli bir bool ekleyerek Callback nesne geliştirebiliriz. Aynı zamanda Callback iki nesne karşılaştırmak için değil beri eşitlik operatörleri devre dışı bırakmak için iyi bir fikirdir. Daha da iyisi, kısmi bir uzmanlık sağlamak için kullanmaktır "sözdizimi" tercih edilir. Bu bize verir:

template<typename FuncSignature>
class Callback;

template<typename R, typename A1>
class Callback<R (A1)>
{
public:
    typedef R (*FuncType)(void*, A1);

    Callback() : obj(0), func(0) {}
    Callback(void* o, FuncType f) : obj(o), func(f) {}

    R operator()(A1 a1) const
    {
        return (*func)(obj, a1);
    }

    typedef void* Callback::*SafeBoolType;
    operator SafeBoolType() const
    {
        return func != 0? &Callback::obj : 0;
    }

    bool operator!() const
    {
        return func == 0;
    }

private:
    void* obj;
    FuncType func;
};

template<typename R, typename A1> // Undefined on purpose
void operator==(const Callback<R (A1)>&, const Callback<R (A1)>&);
template<typename R, typename A1>
void operator!=(const Callback<R (A1)>&, const Callback<R (A1)>&);

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag
{
    template<R (T::*Func)(A1)>
    static R Wrapper(void* o, A1 a1)
    {
        return (static_cast<T*>(o)->*Func)(a1);
    }

    template<R (T::*Func)(A1)>
    inline static Callback<R (A1)> Bind(T* o)
    {
        return Callback<R (A1)>(o, &DeduceMemCallbackTag::Wrapper<Func>);
    }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1))
{
    return DeduceMemCallbackTag<R, T, A1>();
}

#define BIND_MEM_CB(memFuncPtr, instancePtr) \
    (DeduceMemCallback(memFuncPtr).Bind<(memFuncPtr)>(instancePtr))

Kullanım örneği:

class Foo
{
public:
    float DoSomething(int n) { return n / 100.0f; }
};

float InvokeCallback(int n, Callback<float (int)> callback)
{
    if(callback) { return callback(n); }
    return 0.0f;
}

int main()
{
    Foo f;
    float result = InvokeCallback(97, BIND_MEM_CB(&Foo::DoSomething, &f));
    // result == 0.97
    return 0;
}

Visual C derleyici (sürüm 15.00.30729.01, VS 2008 ile birlikte gelen bir) bu test ettik, ve oldukça yeni bir derleyici kodu kullanmanız gerekir. Demontaj muayene, derleyici sarıcı işlevi ve DeduceMemCallback arama en uzak mümkün, basit pointer atamaları için azaltır.

Basit bir geri her iki taraf için de kullanmak, ve tek olduğuna inandığım () standart C kullanır . Yukarıda gösterdiğim kod 1 değişkeni ile üye fonksiyonları çalışıyor ama daha fazla bağımsız değişken için genelleştirilmiş olabilir. Ayrıca statik işlevleri için destek vererek genellenemez.

Callback nesne yığın ayırma - sabit bir boyutu bu "" işlem. standardizasyon sayesinde onlar gerektirdiğini unutmayın Bu nedenle, varsayılan bir oluşturucuya sahip olduğu için olanaklıdır Callback bir nesne daha büyük bir sınıfın bir üyesi olmak zorunda. Ayrıca atanabilir (atama fonksiyonları yeterli derleyici oluşturulan kopya). Ayrıca şablonlar typesafe, teşekkürler.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Derek Banas

    Derek Banas

    12 AĞUSTOS 2008
  • Mark Halberstadt

    Mark Halbers

    19 ŞUBAT 2010
  • RobertDuskin

    RobertDuskin

    12 HAZİRAN 2008