Существует ли способ применить действие к участникам класса C++ N в цикле по именам элемента (вероятно, через препроцессор)?

Если Вы можете достигнуть удаленного сервера в Интернете с помощью IP-адреса, но не доменным именем, то, скорее всего, необходимо обновить серверы DNS:

  1. Редактирование/etc/resolv.conf:

    sudo vi /etc/resolv.conf
    
  2. , Если какие-либо строки сервера имен появляются, запишите IP-адреса для дальнейшего использования. Замените строки сервера имен или добавьте, следующие строки:

    Для IPv4:

    nameserver 8.8.8.8
    nameserver 8.8.4.4
    

    Для IPv6:

    nameserver 2001:4860:4860::8888
    nameserver 2001:4860:4860::8844
    
  3. Сохраняют и выходят.

  4. Перезапуск любые интернет-клиенты Вы используете.
  5. Тест, что Ваша установка работает правильно:

    ping google.com
    

Ссылка: Google Public DNS

7
задан Brian Tompsett - 汤莱恩 8 June 2016 в 11:45
поделиться

12 ответов

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

Поскольку вам явно требовалась возможность объявления списка идентификаторов, вот решение, которое лучше соответствует вашим потребностям.

#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/stringize.hpp>

// sequence of member names (can be declared in a separate header file)
#define MEMBERS (foo)(bar)

// macro for the map example
#define GET_FROM_MAP(r, map, member) member = map[BOOST_PP_STRINGIZE(member)];

BOOST_PP_SEQ_FOR_EACH(GET_FROM_MAP, mymap, MEMBERS)
// generates
// foo = mymap["foo"]; bar = mymap["bar];

-------

//Somewhere else, we need to print all the values on the standard output:
#define PRINT(r, ostream, member) ostream << member << std::endl;

BOOST_PP_SEQ_FOR_EACH(PRINT, std::cout, MEMBERS)

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

6
ответ дан 6 December 2019 в 05:55
поделиться

Boost включает в себя отличную библиотеку препроцессора, которую вы можете использовать для генерации такого кода:

#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/cat.hpp>

typedef std::map<std::string, int> SomeMap;

class MyClass
{
public:
    int intMember1, intMember2, intMember3;

    MyClass(SomeMap & map) 
    {
        #define ASSIGN(z,n,_) BOOST_PP_CAT(intMember, n) = map[ BOOST_PP_STRINGIZE(BOOST_PP_CAT(intMember, n))];
        BOOST_PP_REPEAT_FROM_TO(1, 4, ASSIGN, nil)
    }
};
9
ответ дан 6 December 2019 в 05:55
поделиться

Конечно, возникает очевидный вопрос: Почему у вас класс со 100 членами? Это действительно не кажется разумным.

Если, тем не менее, это нормально - смотрели ли вы библиотеку препроцессоров ускорения ? Я никогда не использовал его сам (как говорил один друг: это ведет к темной стороне), но, насколько я слышал, это должен быть инструмент для работы.

4
ответ дан 6 December 2019 в 05:55
поделиться

Для создания конструктора незаметно используйте perl на своей машине. Затем попросите увеличить вам зарплату, так как вы успешно поддерживаете такой огромный кусок кода.

3
ответ дан 6 December 2019 в 05:55
поделиться

Вы можете использовать препроцессор для определения членов, а затем использовать то же определение для доступа к ним:

#define MEMBERS\
  MEMBER( int, value )\
  SEP MEMBER( double, value2 )\
  SEP MEMBER( std::string, value3 )\

struct FluctuatingMembers {
#define SEP ;
#define MEMBER( type, name ) type name
MEMBERS
#undef MEMBER
#undef SEP
};


.. client code:
FluctuatingMembers f = { 1,2., "valuesofstringtype" };
std::cout <<
  #define SEP <<
  #define MEMBER( type, name ) #name << ":" << f.##name
  MEMBERS;
  #undef MEMBER
  #undef SEP

Это сработало для меня, но его трудно отладить.

2
ответ дан 6 December 2019 в 05:55
поделиться

Вы также можете реализовать шаблон посетителя на основе указателя на элементы. После препроцессора это решение оказывается более отлаживаемым.

struct FluctuatingMembers {
    int v1;
    double v2;
    std::string v3;
    template<typename Visitor> static void each_member( Visitor& v );
};

template<typename Visitor> void FluctuatingMembers::each_member( Visitor& v ) {
  v.accept( &FluctuatingMembers::v1 );
  v.accept( &FluctuatingMembers::v2 );
  v.accept( &FluctuatingMembers::v3 );
}


struct Printer {
    FluctuatingMembers& f;
    template< typename pt_member > void accept( pt_member m ) const {
        std::cout << (f::*m) << "\n";
    }
};

// you can even use this approach for visiting
// multiple objects simultaneously
struct MemberComparer {

    FluctuatingMembers& f1, &f2;
    bool different;
    MemberComparer( FluctuatingMembers& f1, FluctuatingMembers& f2 )
      : f1(f1),f2(f2)
      ,different(false)
    {}

    template< typename pt_member > void accept( pt_member m ) {
      if( (f1::*m) != (f2::*m) ) different = true;          
    }
};

... client code:
FluctuatingMembers object1 = { 1, 2.2, "value2" }
                 , object2 = { 1, 2.2, "valuetoo" };

Comparer compare( object1, object2 );
FluctuatingMembers::each_member( compare );
Printer pr = { object1 };
FluctuatingMembers::each_member( pr );
2
ответ дан 6 December 2019 в 05:55
поделиться

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

4
ответ дан 6 December 2019 в 05:55
поделиться

Почему бы не сделать это во время выполнения? (Я действительно ненавижу хакерство с использованием макросов)

То, что вы действительно просите, в некотором смысле, - это метаданные класса.

Так что я бы попробовал что-нибудь вроде:

class AMember{
 ......
};

class YourClass{
    AMember member1;
    AMember member2;
    ....
    AMember memberN;
    typedef AMember YourClass::* pMember_t;
    struct MetaData : public std::vector<std::pair<std::string,pMember_t>>{
        MetaData(){
            push_back(std::make_pair(std::string("member1"),&YourClass::member1));
            ...
            push_back(std::make_pair(std::string("memberN"),&YourClass::memberN)); 
        }
    };

    static const MetaData& myMetaData() {
        static const MetaData m;//initialized once
        return m;
    }

    YourClass(const std::map<std::string,AMember>& m){
        const MetaData& md = myMetaData();
        for(MetaData::const_iterator i = md.begin();i!= md.end();++i){
            this->*(i->second) = m[i->first];
        }
    }
    YourClass(const std::vector<std::pair<std::string,pMember_t>>& m){
        const MetaData& md = myMetaData();
        for(MetaData::const_iterator i = md.begin();i!= md.end();++i){
            this->*(i->second) = m[i->first];
        }
    }
};

(почти уверен, что синтаксис у меня правильный, но это сообщение о машинах, а не сообщение с кодом)

RE: в функции каждый член имеет тот же точный код, что и другие члены, например, присвоение из карты в конструкторе, где ключ карты такой же, как ключ элемента

, это обрабатывается выше.

RE: Список членов очень гибкий, с постоянными добавлениями и иногда удалениями, некоторые (но не все) управляются изменением столбцов в таблице БД.

Когда вы добавляете новый AMember, скажем newMember, все, что вам нужно do обновляет конструктор MetaData с помощью:

 push_back(make_pair(std::string("newMember"),&YourClass::newMember)); 

RE: Эта идентичность поведения повторяется во многих-многих функциях (> 20), конечно, поведение в каждой функции разное, поэтому нет никакого способа вычленить вещи.

У вас есть механизм, чтобы применить ту же идиому к создать функции

, например: setAllValuesTo (const AMember & value)

 YourClass::setAllValuesTo(const AMember& value){
    const MetaData& md = myMetaData();
    for(MetaData::const_iterator i = md.begin();i!= md.end();++i){
        this->*(i->second) = value;
    }
 }

Если вы немного изобретательны с указателями функций или функционалами шаблонов, вы можете исключить операцию изменения и делать все, что вы хотите, с AMember YourClass на основа коллекции. Оберните эти общие функции (которые могут принимать функциональный указатель или указатель на функцию), чтобы реализовать ваш текущий набор из 20 общедоступных методов в интерфейсе.

Если вам нужно больше метаданных, просто увеличьте кодомен карты MetaData за пределы указателя на член. (Конечно, тогда i-> секунда выше изменится)

Надеюсь, это поможет.

1
ответ дан 6 December 2019 в 05:55
поделиться

Вы можете сделать что-то вроде его:

#define DOTHAT(m) m = map[#m]
DOTHAT(member1); DOTHAT(member2);
#undef DOTHAT

Это не полностью соответствует вашему описанию, но ближе всего к нему, что избавляет от набора текста.

0
ответ дан 6 December 2019 в 05:55
поделиться

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

Добавьте какой-нибудь язык шаблонов в свои исходные файлы. Для чего-то вроде этого вам не нужно реализовывать полноценный парсер или что-то подобное. Просто найдите легко узнаваемый символ в начале строки и несколько ключевых слов, которые нужно заменить.

Используйте приложение командной строки для преобразования шаблонных исходных файлов в реальные исходные файлы. В большинстве систем сборки это должно быть довольно легко сделать автоматически, добавив этап сборки или просто указав системе сборки: «используйте MyParser.exe для обработки файлов типа * .tmp»

Вот пример того, что я ' я говорю о:

MyClass.tmp

MyClass::MyClass(SomeMap & map) { // construct an object from a map
▐REPLACE_EACH, LABEL, "intMember1", "intMember2, ... , "intMemberN"
▐   LABEL = map["$Label"];
}

Я использовал "▐" в качестве примера,

0
ответ дан 6 December 2019 в 05:55
поделиться

Возможно, я бы хотел использовать полиморфизм времени выполнения (динамическую отправку). Создайте родительский класс для этих членов с помощью метода, который выполняет обычные функции. Члены наследуют свой класс от этого родительского класса. Те, которым нужна другая реализация метода, реализуют свои собственные. Если им тоже нужны общие вещи, то внутри метода они могут преобразовать его в базовый класс и вызвать его версию метода.

Затем все, что вам нужно сделать внутри исходного класса, - это вызвать члена для каждого метода.

0
ответ дан 6 December 2019 в 05:55
поделиться

Утечки также отметили open_handle_to_dylib_path в CoreGraphics для меня; здесь определенно что-то отмечено. Поскольку это всего лишь 256 байт, я отправил в Apple отчет об ошибке и назвал его готовым. Затем вы можете установить правило в конфигурации Leaks, чтобы игнорировать отчет.

Как указал bbum, здесь может быть серьезное вуду. Я серьезно сомневаюсь, что Apple позволила бы дырявым фреймворкам проходить контроль качества - тем более, что их так легко воспроизвести.

  • Это абсолютно зависит от каждого члена одного типа. Хотя это было разрешено OP, все же следует оценить, может ли это измениться в будущем. Некоторые другие решения не имеют этого ограничения.
  • Если я правильно помню, offsetof определен только для работы с типами POD стандартом C ++. На практике я ни разу не видел, чтобы это было неудачно. Однако я не использовал все компиляторы C ++. В частности, я никогда не использовал GCC. Поэтому вам нужно будет протестировать это в своей среде, чтобы убедиться, что он действительно работает так, как задумано.
  • Является ли какая-либо из этих проблем - это то, что вам нужно будет оценить в вашей собственной ситуации.


    Теперь, предполагая, что этот метод можно использовать, есть одно приятное преимущество. Эти функции GetMemberX можно превратить в общедоступные статические функции / функции-члены вашего класса, тем самым предоставляя этому универсальному члену доступ к большему количеству мест в вашем коде.

    class MyClass
    {
    public:
        MyClass(SomeMap& Map);
    
        int Member1;
        int Member2;
        int Member3;
    
        static size_t GetMemberCount(void);
        static const char* GetMemberID(size_t i);
        int* GetMemberPtr(size_t i) const;
    };
    

    И если это полезно, вы также можете добавить функцию GetMemberPtrByID в поиск по заданному строковому идентификатору и возврат указателя на соответствующий член.


    Одним из недостатков этой идеи на данный момент является то, что существует риск того, что член может быть добавлен в класс, но не в массив MyClassMembers. Однако этот метод можно комбинировать с макро-решением xtofl, чтобы один список мог заполнять и класс, и массив.

    изменения в заголовке:

    #define MEMBERS\
        MEMBER( Member1 )\
        SEP MEMBER( Member2 )\
        SEP MEMBER( Member3 )\
    
    class MyClass
    {
    public:
        #define SEP ;
        #define MEMBER( name ) int name
        MEMBERS;
        #undef MEMBER
        #undef SEP
    
        // other stuff, member functions, etc
    };
    

    и изменения в файле кода:

    const MemberData MyClassMembers[] = 
    {
        #define SEP ,
        #define MEMBER( name ) { offsetof(MyClass, name), #name }
        MEMBERS
        #undef MEMBER
        #undef SEP
    };
    

    Примечание: у меня есть оставил здесь проверку ошибок из моих примеров. В зависимости от того, как это будет использоваться, вы можете убедиться, что границы массива не выходят за границы с помощью утверждений режима отладки и / или проверок режима выпуска, которые вернут указатели NULL для плохих индексов. Или какое-то использование исключений, если это уместно.

    Конечно, если вас не беспокоит ошибка при проверке границ массива, тогда GetMemberPtr может быть фактически изменен на что-то другое, которое вернет ссылку на член .

    0
    ответ дан 6 December 2019 в 05:55
    поделиться
    Другие вопросы по тегам:

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