Существует ли хороший способ установить членские переменные C/C++ от строковых представлений? (облегченный самоанализом)

Даже при том, что @Rick имеет принятый ответ для этого вопроса, существует на самом деле более короткий способ сделать это, с помощью плохо именованный Uri.GetLeftPart() метод.

Uri url = new Uri("http://www.mywebsite.com:80/pages/page1.aspx");
string output = url.GetLeftPart(UriPartial.Authority);

существует одна выгода к GetLeftPart(), как бы то ни было. Если порт будет портом по умолчанию для схемы, то это разделит его. Так как порт 80 является портом по умолчанию для http, вывод GetLeftPart() в моем примере выше будет http://www.mywebsite.com.

, Если бы номер порта был чем-то другим, чем 80, он был бы включен в результат.

5
задан Community 23 May 2017 в 12:01
поделиться

8 ответов

Если все они имеют один и тот же тип, вы можете использовать что-то вроде этого:

std::map<std::string,int MyType::*> mapper;
mapper["fieldA"]=&MyType::fieldA;
mapper["fieldB"]=&MyType::fieldB;
...
MyType obj;
obj.*(mapper["fieldA"])=3;
6
ответ дан 18 December 2019 в 13:15
поделиться

Смерть макросам.

Некоторый код без макросов, который я использовал в прошлом для привязки имен к членам структуры и преобразования нестроковых типов в строковые:

#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;
}
4
ответ дан 18 December 2019 в 13:15
поделиться

Я могу придумать два решения.

Используйте макросы для создания определения структуры и ее карты из того же source

Используя макросы и переработав определение структуры, вы можете использовать технику, описанную в этом отличном ответе, без отдельного объявления карты.

Перепишите определение структуры, как это, и поместите его в заголовок отдельно:

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

Затем #include дважды. Перед # включением в первый раз:

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

Перед # включением во второй раз:

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

Я не тестировал это, поэтому некоторые детали могут быть неточными.

Если макросы запрещены в вашей среде, вы можете вместо этого создать определение структуры и ее карту из любого внешнего инструмента, который вы пожелаете (Perl, Python Cog и т. Д.).

Используйте библиотеку отражения для C ++

Хотя C ++ не реализует напрямую рефлексию или интроспекцию, доступны дополнительные библиотеки. Я использовал библиотеку Reflex ROOT с хорошими результатами.

2
ответ дан 18 December 2019 в 13:15
поделиться

Эмуляция интроспекции? Звучит как вызов, это точно.

Интерфейс мне не очень нравится, поэтому я предлагаю альтернативу:

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");

Конечно, применяется обычное предостережение: off макушка моей головы, никогда не собирал, может убить котят и того хуже.

1
ответ дан 18 December 2019 в 13:15
поделиться

Если вы хотите перейти от структуры к другому типу данных, у вас есть несколько других вариантов.

Если все поля одного типа, просто используйте карту STL :

typedef std :: map 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
ответ дан 18 December 2019 в 13:15
поделиться

Это более или менее то, для чего предназначен оператор «<<» . К сожалению, язык не предоставляет версию по умолчанию для структур и классов, как это делает для оператора присваивания, но вы можете легко создать свою собственную.

0
ответ дан 18 December 2019 в 13:15
поделиться

Есть ли причина, по которой словарь / карта не работают? т работать? Вы можете хэшировать строки, чтобы ускорить поиск.

0
ответ дан 18 December 2019 в 13:15
поделиться

Если вы не желаете менять структуру на что-то другое, у вас действительно нет выбора - вы собираетесь нужен большой оператор if, чтобы указать, с каким полем вы имеете дело. Вы можете скрыть это (и упростить запись) с помощью макросов, но это та же структура, и вы ' нам придется просто разобраться с этим.

Вот пример того, как вы могли бы написать этот макрос - он действительно упрощает использование, но он все еще не «короткий».

//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"); }};
1
ответ дан 18 December 2019 в 13:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: