2009-08-19 14 views
7

Sorunun oldukça yaygın olduğunu düşünüyorum. Bazı giriş dizeniz var ve dizenin içeriğine bağlı olarak bir işlevi çağırmanız gerekiyor. Dizeler için bir anahtar() gibi bir şey. Komut satırı seçeneklerini düşünün.En şık kod dağıtıcıyı arıyor

using std::string; 

void Myclass::dispatch(string cmd, string args) { 
    if (cmd == "foo") 
     cmd_foo(args); 
    else if (cmd == "bar") 
     cmd_bar(args); 
    else if ... 
     ... 
    else 
     cmd_default(args); 
} 

void Myclass::cmd_foo(string args) { 
... 
} 

void Myclass::cmd_bar(string args) { 
... 
} 

ve başlık

class Myclass { 
    void cmd_bar(string args); 
    void cmd_foo(string args); 
} 

Yani ben her foo ve çubuk dört (4!) Kez tekrarlamak:

Şu anda kullanıyorum. Fonksiyon işaretçisini ve dizeleri önce statik bir diziye besleyebileceğimi ve bir döngü içinde gönderimi yapabildiğimi biliyorum. Ancak, bir şekilde işlevi tanımlamak ve aynı zamanda diziyi otomatik olarak güncellemek mümkün kılan bazı makro hileler (veya POV'ye bağlı önişlemci kötüye kullanımı) var mı? Bu yüzden sadece iki kez mi yazmalıyım, ya da satır içi kullanılırsa bir kez yazmalı mıyım?

C veya C++ 'da bir çözüm arıyorum. Böyle Command pattern

Something aradığınız gibi

+4

Bu soru defalarca sorulmuştur ve bana göre burada cevaplandırılmıştır http://stackoverflow.com/questions/659581/replace-giant-switch-statement-with-what –

+2

Ayrıca, bir çeşit kayıt şeması da istiyor Sözlüğü sürdürme çabasını azaltmak. – djna

+0

O, ama onu tatmin edecek bir şey olduğunu düşünmüyorum. Makrolarla kaba ve çirkin bir şey yapabilirdiniz, ama değerinden daha fazla sorun. –

cevap

1

çirkin makro çözüm:>işlev işaretçileri - Bu

myMap["foo"] = new CommandFoo("someArgument"); 
myMap["bar"] = new CommandBar("anotherArgument"); 
+0

Eh, verilen, çirkin, ama ikiye kadar tekrarları kesilmiş gibi görünüyor. Ama kodun ortasında bir #include gerçekten çirkin. Aynı sebepten dolayı +1 – hirschhornsalz

+1

+1. Ayrıca, her bir "foo" komutunun cmd_foo çağırmasıyla sonuçlandığını varsayarak, bir miktar stringifikasyon yaparak, parametrelerde tekrarlamayı önleyebilirsiniz. –

+0

Evet, "paste" operatörünün satırlarını düşünürüm. – hirschhornsalz

8

geliyor:

sonra sadece bu gibi komut çalıştırmak için anahtar kullanmak bu

std::map<std::string, Command*> myMap; 

gibi bir harita oluşturun. ...

std::map<std::string, Command*>::iterator it = myMap.find(str); 
if(it != myMap.end()) { 
    it->second->execute() 
} 

Sadece komutlarınızı kaydetmek için Eğer tür-of istedi

typedef void (*cmd)(string); 
+0

Hayır. Muhtemelen "cmd_ *" adlı işlevleri kullanmam konusunda yanıltıcıdır, ancak farklı bir şey olabilir, komutları temsil etmemeye gerek yoktur. Ekleyicinin ek olarak bir nesneye bağlı olması gerekmez. – hirschhornsalz

+2

@drhirsch. Gerçek komutlar olmak zorunda değiller. Bu sadece desenin adı. Temel olarak, bir giriş – Glen

+0

'a dayanarak önceden tanımlanmış bir kodu yürütmenin bir yolu, Neil ve Glen'in önerdiği çözümün doğru cevap olduğunu düşünürdüm. Kendinden kayıt içermez, ancak C++ 'da bunu yapmanın temiz bir yolu olduğundan emin değilim. Java veya C# 'da olsa, çok uygun olurdu. –

2

as alternative to the Command pattern Eğer bir hashtabledize inşa edebilirsiniz. Otomatik olarak kayıt olmadığına dikkat edin, ancak bazı şeylerin senkronize olmasını sağlar ve kaynak dosyadaki işlevi değil, yalnızca eşlemelere eklediğinizde derleme hatalarına neden olur.

Mappings.h:

// Note: no fileguard 
// The first is the text string of the command, 
// the second is the function to be called, 
// the third is the description. 
UGLY_SUCKER("foo", cmd_foo, "Utilize foo."); 
UGLY_SUCKER("bar", cmd_bar, "Turn on bar."); 

Parser.h:

class Myclass { 
... 
protected: 
    // The command functions 
    #define UGLY_SUCKER(a, b, c) void b(args) 
    #include Mappings.h 
    #undef UGLY_SUCKER 
}; 

Parser.cpp:

void Myclass::dispatch(string cmd, string args) { 
    if (cmd == "") 
     // handle empty case 
#define UGLY_SUCKER(a, b, c) else if (cmd == a) b(args) 
#include Mappings.h 
#undef UGLY_SUCKER 
    else 
     cmd_default(args); 
} 

void Myclass::printOptions() { 
#define UGLY_SUCKER(a, b, c) std::cout << a << \t << c << std::endl 
#include Mappings.h 
#undef UGLY_SUCKER 
} 

void Myclass::cmd_foo(string args) { 
... 
} 
+1

Bu, _pattern_ için gerçek bir alternatif değil: Farklı şekilde uygulanan aynı konsept. – xtofl

5

temel çözüm, soru açıklama benim linke başına olduğunu Bir dizeyi bir işlev çağrısına eşlemek için.

aslında dize kaydolmak için -> fonksiyon işaretçisi/funktoru çifti:

Öncelikle, memuru nesne a tek sahip (şok korku!!). Diyelim ki TheDispatcher - map<string,Func> için bir sarıcıdır, burada Func, fonksiyon işaretçiniz veya functor tipinizdir. Eğer statik nesneler oluşturmak bireysel derleme birimlerinde Şimdi

struct Register { 
    Register(comst string & s, Func f) { 
     TheDispatcher.Add(s, f); 
    } 
}; 

: Sonra

, bir kayıt sınıf var (! şok dehşeti!):

Register r1_("hello", DoSayHello); 

Bu nesneler (varsayarak oluşturulacak kod statik bir kütüphanede değildir) ve TheDispatcher ile otomatik olarak kayıt olur.

Ve çalışma zamanında TheDispatcher'da dizeleri ararsınız ve ilgili işlevi/functor'ı çalıştırırsınız.

1

En azından işlevleri tanımlamanız ve bazı kayıt defterlerine eklemeniz gerekir. (Bir sınıfın satır içi üye olmayan işlevleri olacaklarsa, bunları da bildirmeniz gerekir.) Gerçek kod oluşturan bazı alana özgü dil dışındaki (cjhuitt's macro hackery gibi), bu iki işlevi belirtmenin hiçbir yolu göremiyorum. (veya üç) kez.

+0

Evet, sanırım sonunda kalın kafatasına girdim. – hirschhornsalz

İlgili konular