2009-11-06 8 views
5

मुझे कुछ सदस्यों के साथ एक संरचना मिली है जो मैं एक स्ट्रिंग से प्राप्त करने और सेट करने में सक्षम होना चाहता हूं। यह देखते हुए कि सी ++ किसी भी आत्मनिरीक्षण मैं समझ मैं मैक्रो के साथ कुछ रचनात्मक समाधान की जरूरत है, stringize ऑपरेटर और शायद boost::bind. मैं पूरी क्रमबद्धता या आत्मनिरीक्षण की जरूरत नहीं है, और अधिक एक 'आत्मनिरीक्षण-लाइट' नहीं हैस्ट्रिंग प्रस्तुतियों से सी/सी ++ सदस्य चर सेट करने का कोई अच्छा तरीका है? (आत्मनिरीक्षण-लाइट)

मैं चाहूँगा इस की तर्ज पर कुछ है करने के लिए:

struct MyType { 
    int fieldA; 
    int fieldB; 
}; 
DECLARE_STRING_MAP(MyType,fieldA); 
DECLARE_STRING_MAP(MyType,fieldB); 

MyType t; 
SET_VALUE_FROM_STRING(MyType,t,"fieldA","3")  

के बजाय एक विशाल if बयान की है।

कोई विचार अगर इस के लिए एक साफ समाधान है?

संबंधित प्रश्न: Object Reflection

संपादित करें: 'प्रकार int :: * नक्शा' चाल के लिए maxim1000 को धन्यवाद - यह मेरे लिए काम किया:

#define DEFINE_LOOKUP_MAP(Type) std::map<AnsiString,int Type::*> mapper 
#define ADD_FIELD_MAPPING(Type, Field) mapper[#Field]=&Type::Field 
#define SET_FIELD_FROM_MAP(Type, Field, var, value) var.*(mapper[#Field])=value  

DEFINE_LOOKUP_MAP(MyType); 
ADD_FIELD_MAPPING(MyType, fieldA); 
ADD_FIELD_MAPPING(MyType, fieldB); 

SET_FIELD_FROM_MAP(MyType, fieldA, obj, 3); 
+0

मुझे लगता है कि आप SET_VALUE_FROM_STRING लिखने के लिए (MyType चाहते हैं, टी , currentAttr, "3") साथ ही? –

+0

बढ़ावा :: lexical_cast भी उपयोगी हो सकता है। –

+0

@Matthieu: हाँ, तीसरा तर्क एक स्ट्रिंग वैरिएबल हो सकता है, उदाहरण के लिए, मैं 'फ़ील्ड ए = 3, फ़ील्डबी = 10'' फॉर्म के कुछ पाठ को पार्स कर सकता हूं और इस फ़ंक्शन में कॉल कर सकता हूं @ltcmelo - yes, 'lexical_cast' टाइप रूपांतरण के लिए उपयोगी हो, लेकिन चलिए इस पल के लिए मान लें कि मुझे पता है कि मान हमेशा इंट्स होने जा रहे हैं। –

उत्तर

5

तो उन सभी को है एक ही प्रकार के हैं, तो आप कुछ इस तरह का उपयोग कर सकते हैं:

std::map<std::string,int MyType::*> mapper; 
mapper["fieldA"]=&MyType::fieldA; 
mapper["fieldB"]=&MyType::fieldB; 
... 
MyType obj; 
obj.*(mapper["fieldA"])=3; 
+0

निश्चित रूप से इस तरह से 'माईटाइप :: फ़ील्ड ए' के ​​लिए बाध्यकारी के संदर्भ में सिंटैक्स का प्रकार है, हालांकि मुझे आश्चर्य है कि मानचित्र के निर्माण की आवश्यकता के बिना ऐसा करना संभव है –

+1

@the_mandrill: कुछ स्पष्ट मानचित्रण को लेना है जगह क्योंकि परिवर्तनीय नाम संकलित समय पर "खो गया" हैं। – luke

+1

@ maxim1000: इस समाधान के डाउनसाइड्स में से एक गलत सदस्य स्ट्रिंग निर्दिष्ट करने का खतरा है (यानी, मानचित्र में मौजूद नहीं है) और बाद में शून्य अव्यवस्था पर क्रैश हो रहा है। – luke

1

आप कुछ और करने के लिए struct को बदलने के लिए तैयार नहीं हैं, तो आप वास्तव में एक विकल्प नहीं है - आप कर रहे हैं जी यदि आप यह बताते हैं कि आप किस क्षेत्र से बात कर रहे हैं तो बयान देने के लिए बड़ी आवश्यकता होगी। मैक्रोज़ के साथ आप इसे छिपा सकते हैं (और इसे लिखना आसान बनाते हैं), लेकिन यह वही संरचना है, और आपको बस इससे निपटना होगा।

यहां एक उदाहरण है कि आप उस मैक्रो को कैसे लिख सकते हैं - यह उपयोग को आसान बनाता है, लेकिन यह अभी भी किसी भी माध्यम से 'छोटा' नहीं है।

//Assumption: at the time you want to use this, you've got two strings, one with the 
// name of the field to set (key), one with the value to set (value). I also assume 

typedef struct MyType { 
    int fieldA; 
    int fieldB; 
} MyType; 

// fldnamedef - name of the field in the structure definition (const char *) 
// convfunc - name of a function that takes a value, returns a fldtypedef 
// s - structure to put data into 
// key - const char * pointing to input field name 
// value - const char * pointing to input field value 
#define IF_FIELD_SET(fldnamedef, convfunc, s, key, value) {\ 
    if (strcmp(#fldnamedef, key) == 0) {\ 
    s.fldnamedef = convfunc(value);\ 
    }\ 
} 


int main() 
{ 
    MyType t={0,0}; 

    IF_FIELD_SET(fieldA, atoi, t, "fieldA", "2"); 

    printf("%d,%d\n",t.fieldA, t.fieldB); 
} 

और यहाँ पूर्व प्रोसेसर उत्पादन कि IF_FIELD_SET लाइन में बदल जाता है है:

{ if (strcmp("fieldA", "fieldA") == 0) { t.fieldA = atoi("2"); }}; 
+0

के लिए अधिक स्वीकार्य व्यवहार के साथ कार्यान्वित किया जाना चाहिए, मुझे लगता है कि आपको कुछ याद आया: "fldnamedef" यह बेकार बनाता है, यह लिखने का एक लंबा रास्ता है ' t.fieldA = atoi ("2"); '... वहां कोई आत्मनिरीक्षण नहीं है। –

+0

सी ++ ओपी वास्तव में जिस तरह से जरूरत है में आत्मनिरीक्षण नहीं करता है। ओपी के विकल्प मैक्रोज़ में हैं या डेटा संरचनाओं को स्विच करते हैं। यह उत्तर मैक्रोज़ में आईएफएस को लपेटने का एक तरीका है जिससे इसे काम करना थोड़ा आसान हो जाता है। –

0

यह कम या ज्यादा क्या "< <" ऑपरेटर के लिए है। अफसोस की बात यह है कि भाषा स्ट्रक्चर और कक्षाओं के लिए एक डिफ़ॉल्ट संस्करण प्रदान नहीं करती है जैसे कि असाइनमेंट ऑपरेटर के लिए करता है, लेकिन आप अपना खुद का पर्याप्त आसानी से बना सकते हैं।

0

यदि आप किसी संरचना से दूसरे डेटा प्रकार में बदलने के इच्छुक हैं, तो आपके पास कुछ अलग-अलग विकल्प हैं।

क्षेत्रों में एक ही प्रकार के सभी कर रहे हैं, सिर्फ एक एसटीएल मानचित्र का उपयोग करें:

typedef std :: नक्शा MyType;

MyType t; 

t["fieldA"] = atoi("3"); 
printf("%d\n", t["fieldA"]); 

वे अलग अलग प्रकार के होते हैं, तो आप मान परिवर्तित करने के साथ जा सकते हैं जब आप उन्हें संरचना से बाहर ले:

typedef std::map<std::string, std::string> MyType; 

MyType t; 
t["fieldA"] = "3"; 

printf("%d\n", atoi(t["fieldA"])); 

आप प्राप्त और में रूपांतरण लपेट कर सकते हैं क्षेत्र-विशेष मैक्रोज़, लिखने के लिए थोड़ा आसान बनाने के लिए।

typedef std::map<std::string, std::string> MyType; 
#define fieldA(v) atoi(v["fieldA"]) 

MyType t; 
t["fieldA"] = "3"; 

printf("%d\n", fieldA(v)); 

उसमें संरचना तत्व पहुंच की तरह दिखने का नुकसान नहीं होता है।

आप MyType को कक्षा बनाने और प्रत्येक फ़ील्ड के लिए व्यक्तिगत कार्यों का उपयोग करने का प्रयास कर सकते हैं। यह कम से कम आपको प्रति फ़ील्ड के विभिन्न प्रकार प्राप्त करने की अनुमति देता है, लेकिन आपको अभी भी सेट करने के लिए ifs का बड़ा ब्लॉक होना होगा। बेशक, चूंकि आप इसे ऑब्जेक्ट में डाल सकते हैं, इसका उपयोग करना आसान होगा। बेशक, आपने ऑब्जेक्ट विधि कॉल में संरचना फ़ील्ड का उपयोग कर दिया है। फिर भी, इसका उपयोग करना बहुत आसान है, और यह आपको कुछ खरीद सकता है।

class MyType { 
public: 
    set(std::string key, std::string value) { 
    if (key == "fieldA") m_fieldA = atoi(value.c_str()); 
    if (key == "fieldB") m_fieldB = atoi(value.c_str()); 
    }; 

    int fieldA() { return m_fieldA; }; 
    int fieldB() { return m_fieldB; }; 
private: 
    int m_fieldA; 
    int m_fieldB; 
}; 

MyType t; 
t.set("fieldA", "3"); 
printf("%d\n", t.fieldA()); 
0

क्या कोई कारण है कि कोई शब्दकोश/मानचित्र काम नहीं करेगा? आप लुकअप को तेज बनाने के लिए तारों को हश कर सकते हैं।

1

आत्मनिरीक्षण अनुकरण? यह एक चुनौती की तरह लगता है, यह निश्चित रूप से है।

इंटरफ़ेस वास्तव में मुझे खुश नहीं है, तो मैं एक विकल्प का प्रस्ताव होगा:

struct MyType 
{ 
    int fieldA; 
    int fieldB; 

    void setField(std::string const& field, std::string const& value); 
}; 

अब चुनौती setField सही क्षेत्र का चयन करने के लिए है, और वास्तव में एक नक्शा उचित लगता है। हालांकि हमें कहीं भी प्रकार की जानकारी को समाहित करने की आवश्यकता है (जब तक कि आप केवल इनट्स का उपयोग करने की योजना नहीं बनाते, इस मामले में ... कोई कठिनाई नहीं है), इसलिए मज़दूरों का नक्शा क्रम में है।

static std::map<std::string, Functor<MyType>*> M_Map; 

// where Functor is 

template <class Type> 
struct Functor 
{ 
    virtual void set(Type& t, std::string const& value) const = 0; 
}; 

// And a specialization would be 
struct SetfieldA : public Functor<MyType> 
{ 
    virtual void set(MyType& t, std::string const& value) const 
    { 
    std::istringstream stream(value); 
    stream >> t.fieldA; 
    // some error handling could be welcome there :) 
    } 
}; 

नोट std::istringstream के उपयोग, अब आप किसी भी प्रकार के रूप में लंबे समय के रूप में वे सही ढंग से एक std::istream के साथ बातचीत का समर्थन कर सकते हैं। इस प्रकार आप उपयोगकर्ता परिभाषित कक्षाओं का समर्थन कर सकते हैं।

और निश्चित रूप से, यहां हिस्सा ऑटोमेशन के बारे में है!

और मैक्रोज़ में स्वचालन जैसे।

#define INTROSPECTED(MyType_)             \ 
    private:                  \ 
    typedef Functor<MyType_> intro_functor;          \ 
    typedef std::map<std::string, intro_functor const*> intro_map;    \ 
    static intro_map& IntroMap() { static intro_map M_; return M_; }    \ 
    public:                  \ 
    static void IntroRegister(std::string const& field, intro_functor const* f){ \ 
     IntroMap()[field] = f; }             \ 
    void setField(std::string const& field, std::string const& value) {   \ 
     intro_map::const_iterator it = IntroMap().find(field);      \ 
     if (it != IntroMap().end()) it->second->set(*this, value); } 

#define INTROSPECT_FIELD(Class_, Name_)           \ 
    struct Set##Name_: public Functor<Class_> {         \ 
    virtual void set(Class_& t, std::string const& value) {      \ 
     std::istringstream stream(value); stream >> t.Name_; } } Setter##Name_; \ 
    Class_::IntroRegister(#Name_, Setter##Name_) 

प्रयोग इस तरह:

// myType.h 
struct MyType 
{ 
    INTROSPECTED(MyType); 

    int fieldA; 
    int fieldB; 
}; 

// myType.cpp 
INTROSPECT_FIELD(MyType, fieldA); 
INTROSPECT_FIELD(MyType, fieldB); 

// Any file 
MyType t; 
t.set("fieldA", "3"); 
बेशक

सामान्य चेतावनी लागू होते हैं: मेरे सिर के ऊपर से, यह कभी नहीं संकलित, बिल्ली के बच्चे और बदतर मार सकता है।

2

मैं दो समाधानों के बारे में सोच सकता हूं।

उपयोग मैक्रो एक संरचना परिभाषा और एक ही स्रोत

से अपने मानचित्र मैक्रो का उपयोग कर और अपने संरचना परिभाषा दोबारा काम करके बनाने के लिए, आप अलग से नक्शा घोषित करने के बिना इस उत्कृष्ट जवाब में वर्णित के रूप तकनीक का उपयोग कर सकते हैं। # शामिल इसे दो बार फिर

BEGIN_STRUCT(MyType) 
FIELD(int, fieldA); 
FIELD(int, fieldB); 
END_STRUCT 

:

इस तरह से अपनी संरचना परिभाषा पुनर्लेखन, और यह एक शीर्ष लेख में अपने आप में डाल दिया। यह पहली बार #including से पहले:

#define BEGIN_STRUCT(x) struct x { 
#define FIELD(x, y) x y; 
#define END_STRUCT }; 

यह दूसरी बार #including से पहले:

#define BEGIN_STRUCT(x) namespace x ## Mapping { typedef x MappedType; 
#define FIELD mapper[#x]=&MappedType::x; 
#define END_STRUCT } 

मैं इस परीक्षण नहीं किया है, इसलिए कुछ विवरण बंद हो सकता है।

यदि आपके पर्यावरण में मैक्रोज़ पर प्रतिबंध लगा दिया गया है, तो आप अपनी इच्छित संरचना उपकरण और पर्ल, पायथन के Cog इत्यादि से संरचना परिभाषा और उसका मानचित्र बना सकते हैं। सी ++ सीधे प्रतिबिंब या आत्मनिरीक्षण को लागू नहीं करता है

के लिए सी ++

एक प्रतिबिंब लाइब्रेरी का, ऐड-ऑन पुस्तकालयों में उपलब्ध हैं। मैंने अच्छे परिणामों के साथ ROOT's Reflex लाइब्रेरी का उपयोग किया है।

4

मैक्रोज़ की मौत।

कुछ वृहद मुक्त कोड मैं स्ट्रिंग के लिए नाम बाध्यकारी सदस्यों struc करने और परिवर्तित गैर स्ट्रिंग प्रकार के लिए अतीत में उपयोग किया है:

#include <map> 
#include <string> 
#include <sstream> 

template<class STRUC> 
struct Field 
{ 
    virtual void set (STRUC& struc, const std::string& value) const = 0; 
}; 

template<class STRUC, class FIELDTYPE> 
struct FieldImpl : public Field<STRUC> 
{ 
    typedef FIELDTYPE (STRUC::*MemberPtr); 

    FieldImpl (MemberPtr memberPtr) {memberPtr_ = memberPtr;} 

    virtual void set (STRUC& struc, const std::string& value) const 
    { 
     std::istringstream iss (value); 
     iss >> struc.*memberPtr_; 
    } 

private: 
    MemberPtr memberPtr_; 
}; 

template<class STRUC> 
class FieldMap 
{ 
private: 
    typedef std::map<std::string, Field<STRUC>*> FieldNameMap; 
    FieldNameMap fieldMap_; 

public: 
    ~FieldMap() 
    { 
     // delete fieldMap_ members. 
    } 

    void bind (const std::string& name, Field<STRUC>* field) 
    { 
     fieldMap_[name] = field; 
    } 

    template<typename FIELDTYPE> 
    void bind (const std::string& name, FIELDTYPE (STRUC::* member)) 
    { 
     fieldMap_[name] = new FieldImpl<STRUC, FIELDTYPE> (member); 
    } 

    void setValue (STRUC& struc, const std::string& name, const std::string& value) 
    { 
     FieldNameMap::const_iterator iter = fieldMap_.find (name); 

     if (iter == fieldMap_.end()) 
      throw std::runtime_error (std::string ("No field binding found for ") + name); 

     (*iter).second->set (struc, value); 
    } 
}; 

struct Test 
{ 
    int id; 
    double value; 
    std::string tag; 
}; 

int main (int argc, char* argv[]) 
{ 
    FieldMap<Test> fieldMap; 
    fieldMap.bind ("id", &Test::id); 
    fieldMap.bind ("value", &Test::value); 
    fieldMap.bind ("tag", &Test::tag); 

    Test test; 

    fieldMap.setValue (test, "id", "11"); 
    fieldMap.setValue (test, "value", "1234.5678"); 
    fieldMap.setValue (test, "tag", "hello"); 

    return 0; 
} 
संबंधित मुद्दे