сопоставить строки с учениками - PullRequest
0 голосов
/ 20 декабря 2018

Скажем, у меня есть класс / структура, как показано ниже:

struct A {
    uint32_t a;
    uint8_t  b;
    uint16_t c;
};

И у меня есть набор строк, которые связаны с каждым членом A (могут быть разных целочисленных типов, но не целочисленных типовнапример, строки), например

"field1" -> A::a
"field2" -> A::b
"field3" -> A::c

Предположим, что всегда есть соотношение 1: 1 между строками и элементами.Есть ли элегантный способ сопоставить каждую строку каждому члену, используя что-то вроде std :: unordered_map?Я хочу иметь возможность читать и писать в каждое поле, используя строки в качестве ключей, например,

A a {1,2,3};
mymap["field1"] = 4; // a.a = 4
mymap["field2"] = 5; // a.b = 5
auto c = mymap["field3"]; // c = a.c = 3

Я использую C ++ 11/14 и не могу использовать повышение.


Еще немного информации о контексте этого вопроса : struct A, упомянутый в вопросе, является настройками программы.Это параметры конфигурации оборудования, и моя программа используется для имитации поведения оборудования.Эти настройки / конфигурации генерируются скриптом struct s, как указано выше.Мы читаем / пишем эти struct члены, и поскольку количество этих настроек / конфигураций очень много (несколько тысяч), было бы удобно иметь возможность доступа к ним по их именам.Вот как и почему я хочу связать каждого члена со строкой.Является ли это подпиской или функцией доступа к соответствующим элементам, на самом деле не имеет значения, но существует такое соотношение 1: 1 между строкой (имя параметра) и сгенерированным элементом struct.И, как я уже упоминал в этом вопросе, эти члены имеют разные целочисленные типы.

Ответы [ 5 ]

0 голосов
/ 21 декабря 2018

Простое решение состоит в том, что если у вас есть сценарий, который может генерировать структуру, то вы можете иметь свой сценарий , генерирующий ваших сеттеров.

Начните с простого интерфейса функций.

struct A {
    uint32_t a;
    uint8_t  b;
    uint16_t c;
};

typedef std::function<void(A &,string)> SetterType

Затем создайте таблицу поиска

std::map<std::string,SetterType> Lookup;

и для каждого поля, используя ваш скрипт, сгенерируйте анализатор и установщик;

void A_a(A & data, std::string input){
        data.a = std::stoi(input);
}

, а затем

Lookup["a"] = &A_a;

и используйте его как

Lookup["a"]("10");

Если вы не можете изменить генерирующий скрипт, то, возможно, вы можете использовать сторонний парсер, такой как swig или clang чтобы прочитать ваши структуры и сгенерировать вам дерево разбора, которое вы затем сможете использовать для генерации таблиц поиска.

Или просто используйте систему, которая уже отображает строки в C ++.Генератор JSON C ++.

https://nlohmann.github.io/json/

0 голосов
/ 21 декабря 2018

Основная проблема вашего вопроса заключается в том, что разные участники имеют разные типы.Если вы делаете типы одинаковыми, вы можете использовать множество хитростей.

В вашем случае я знаю, что вы не хотите использовать Boost или C ++ 17, но просто для того, чтобы показать вам предстоящие задачиПозвольте мне дать вам решение Boost.Hana, C ++ 17.

#define  BOOST_HANA_CONFIG_ENABLE_STRING_UDL
#include <boost/hana/equal.hpp>
#include <boost/hana/string.hpp>
#include <cstdint> // uint
#include <cassert>

struct A {
    uint32_t a;
    uint8_t  b;
    uint16_t c;
};

struct map{
    A& aa_;
    template<class String>
    decltype(auto) operator[](String s) const{
        using namespace boost::hana::literals;
        if constexpr(s == "field1"_s) return (decltype(aa_.a)&)(aa_.a);
        if constexpr(s == "field2"_s) return (decltype(aa_.b)&)(aa_.b);
        if constexpr(s == "field3"_s) return (decltype(aa_.c)&)(aa_.c);
    }
};

using namespace boost::hana::literals;

void f(uint32_t& a){ a = 3.;}

int main(){
    A aa{1,2,3};
    map mymap{aa};
    mymap["field1"_s] = 4; assert(aa.a == 4);
    mymap["field2"_s] = 5; assert(aa.b == 5);
    mymap["field3"_s] = 6; assert(aa.c == 6);
    auto c = mymap["field3"_s]; assert( c == aa.c );
    mymap["blabla"_s]; // is void (not a compile error)
    assert( map{aa}["field1"_s] == 4 );
}

Отсюда вы можете пойти назад и, возможно, выяснить, C ++ 14, задача в том, чтобы вам пришлось реализоватьваши собственные строковые литералы времени компиляции и равенство.Другими словами, переопределите ваши собственные строки Hana: https://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/namespaceboost_1_1hana_1_1literals.html

0 голосов
/ 21 декабря 2018

Вы можете создать прокси-класс, который будет заключен в целое число, а затем сохранить этот прокси-класс в std::unordered_map.

#include <iostream>
#include <functional>
#include <unordered_map>

struct A {
    uint32_t a;
    uint8_t  b;
    uint16_t c;
};

struct ValueWrapper {
    using value_type = uint64_t;

    template <typename Obj, typename T>
    ValueWrapper(Obj& obj, T Obj::*member) {
        get = [&, member]() { return obj.*member; };
        set = [&, member](value_type value) mutable { obj.*member = value; };
    }

    ValueWrapper()  = default;

    ValueWrapper& operator=(value_type value) {
        set(value);
        return *this;
    }

    operator value_type() {
        return get();
    }

    std::function<value_type()> get;
    std::function<void(value_type)> set;
};

std::unordered_map<std::string, ValueWrapper> make_map(A& a) {
    std::unordered_map<std::string, ValueWrapper> map;

    map["field1"] = ValueWrapper(a, &A::a);
    map["field2"] = ValueWrapper(a, &A::b);
    map["field3"] = ValueWrapper(a, &A::c);

    return map;
}

int main() {
    A a{1,2,3};

    auto map = make_map(a);

    map["field2"] = 67;

    std::cout << a.a << " " << static_cast<int>(a.b) << " " << a.c << std::endl;
    std::cout <<  map["field1"] << " " <<  map["field2"] << " " <<  map["field3"] << std::endl;
}

Вы получите некоторые ограничения в зависимости от value_type.Если вы используете int64_t, вы можете безопасно обернуть все, кроме uint64_t.Если вы выберете uint64_t, вы можете безопасно обернуть все целые числа без знака, но не все без знака.

Я поместил туда конструктор по умолчанию, чтобы удовлетворить unordered_map s использование operator[].

0 голосов
/ 21 декабря 2018

вы сказали, что у него могут быть разные целочисленные типы, но это проблема для элегантного решения.Если вы можете настроить один тип, это будет просто, как показано ниже.

 #include <iostream>
 #include <map>

 struct A {
     int a;
     int b;
     int c;
 };
 using namespace std;

 int main() {
     map<string, int A::*> abc = {
         {"a", &A::a},
         {"b", &A::b},
         {"c", &A::c}
     };

     A aa;

     aa.*abc["a"] = 1;
     aa.*abc["b"] = 2;
     aa.*abc["c"] = 3;

     cout << "a = " << aa.a << "(" << aa.*abc["a"] << ")" << endl;
     cout << "b = " << aa.b << "(" << aa.*abc["b"] << ")" << endl;
     cout << "c = " << aa.c << "(" << aa.*abc["c"] << ")" << endl;

     return 0;
 }
0 голосов
/ 21 декабря 2018
template<class V>
struct pseudo_ref_t {
  operator V()&& { return getter(); }
  void operator=(V v)&&{
    setter(std::move(v));
  }

  std::function<void(V)> setter;
  std::function<V()> getter;
};
template<class T, class V>
struct member_t {
  friend pseudo_ref_t<V> operator->*( T* t, member_t const& self ) {
    return {
      [&self, t](V in){ self.setter(*t, std::move(in)); },
      [&self, t]()->V{ return self.getter(*t); }
    };
  }
  friend V operator->*( T const* t, member_t const& self ) {
    return self.getter(*t);
  }
  std::function<void(T&, V)> setter;
  std::function<V(T const&)> getter;
};
template<class T, class V, class X>
member_t<T, V> make_member( X T::* mem_ptr ) {
  return {
    [mem_ptr](T& t, V in) {
      (t.*mem_ptr) = std::move(in);
    },
    [mem_ptr](T const& t)->V {
      return (t.*mem_ptr);
    }
  };
}

a member_t<A, uint32_t> может стереть тип любого члена A, который неявно преобразуется в / из uint32_t.

. Он действует как указатель интеллектуального элемента.

...