2015-05-15 1 views
0

В проекте на основе графического интерфейса мне нужно Page1 отметить переменную , которую нужно изменить и вызвать страницу2, Страница2 читает ввод пользователя и обновляет отмеченную переменную с помощью новое значение. Тип переменной всегда разный, и все переменные хранятся во внешней связанной библиотеке.Передача указателей на различные типы переменных на функцию и их повторное использование для присваивания

Как достичь этого без создания fname_uint8, fname_uint16, fname_giventype вариантов для маркеров и сеттеров?

Этот пример подводит итог сценария:

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

class VarHolder 
{ 
public: 
    typedef struct { 
     int8_t var1; 
     int16_t var2; 
     int32_t var3; 
     char str1[40]; 
     float var4; 
    } struct1_t; 
    /* ...continues... */ 

struct1_t struct1; 
} 

Теперь класс FirstStage хочет отметьте одну переменную для изменения и называет элемент экземпляра committer_instance класса Committer

class FirstStage 
{ 
    /* ... */ 
    void doFirstStage(void) 
    { 
     /* Globally defined committer instance */ 
     g_committer_instance->mark_var_change(&varholder_instance->struct1.var1); 
    } 
} 

Committer::mark_var_change(T*) определяется следующим образом:

template <typename T> 
void Committer::mark_var_change(T *var) 
{ 
    /* Store a pointer to the variable */ 
    /* SAVE SOMEWHERE PRESERVING TYPE */ = var; 
} 

Член SecondStage, наконец, хочет использовать в настоящее время доступное значение для обновления переменной помеченный для изменения через тот же g_committer_instance, как это:

class SecondStage 
{ 
    /* ... */ 
    template <typename T> 
    void doSecondStage(T new_value) 
    { 
     g_committer_instance->commit_change(new_value); 
    } 
} 

, где Committer::commit_change(T) определяется следующим образом:

template <typename T> 
void Committer::commit_change(T new_value) 
{ 
    /* Dereferencing the previously stored pointer */ 
    *(/*WHATEVER I STORED BEFORE*/) = new_value; 
} 

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

MCVE

### varholder.h 
#include <stdint.h> 
class VarHolder 
{ 
public: 
VarHolder() {} 
virtual ~VarHolder() {} 
typedef struct 
{ 
int8_t var1; 
uint8_t var2; 
int64_t var3; 
char str1[40]; 
} struct1_t; 

struct1_t struct1; 
} 

### firststage.h 
#include global.h 

class FirstStage 
{  
public: 
    FirstStage() {} 
    ~FirstStage() {} 

    void doFirstStage(void) 
    { 
     /* Globally defined committer instance */ 
     g_committer_instance->mark_var_change(&varholder_instance->struct1.var1); 
    } 
} 

### secondstage.h 
#include global.h 

class SecondStage 
{ 
    public: 
    SecondStage() {} 
    ~SecondStage() {} 
    template <typename T> 
    void doSecondStage(T new_value) 
    { 
     g_committer_instance->commit_change(new_value); 
    } 
} 
### committer.h 
#include global.h 

class Committer 
{ 
    public: 
    Committer() {} 
    ~Committer() {} 
template <typename T> 
void Committer::mark_var_change(T *var) 
{ 
    /* Store a pointer to the variable */ 
    /* SAVE SOMEWHERE PRESERVING TYPE */ = var; 
} 

template <typename T> 
void Committer::commit_change(T new_value) 
{ 
    /* Dereferencing the previously stored pointer */ 
    *(/*WHATEVER I STORED BEFORE*/) = new_value; 
} 
} 


### global.h 

#include varholder.h 
#include committer.h 

extern Committer *g_committer_instance; 
extern VarHolder *varholder_instance; 

### main.cpp 

#include global.h 
#include varholder.h 
#include firststage.h 
#include secondstage.h 


Committer *g_committer_instance; 
VarHolder *varholder_instance; 

int main() 
{ 
g_committer_instance = new Committer(); 
varholder_instance = new VarHolder(); 
FirstStage *fstage = new FirstStage(); 
SecondStage *sstage = new SecondStage(); 

int8_t var_new = 100; 

/* First stage */ 
fstage->doFirstStage(); 
/* Second stage */ 
sstage->doSecondStage(var_new); 

return 0; 
} 
+0

Вы можете посмотреть на 'повысить :: any'. – Jarod42

+0

Создайте [MCVE] (http://stackoverflow.com/help/mcve), который описывает вашу проблему. – TobiMcNamobi

+0

Есть только одна переменная для каждого типа в 'struct1_t'? Если да, почему бы не использовать союз? Кроме того, каково будет поведение, если тип 'new_value' не конвертируется в' varholder_instance-> struct1.var1' –

ответ

0

Вы можете использовать void* без (небезопасные) или с typeid:

class Committer 
{ 
public: 

    template <typename T> 
    void mark_var_change(T *var) 
    { 
     mPointer = var; 
     mTypeInfo = &typeid(T*); 
    } 

    template <typename T> 
    void commit_change(T new_value) 
    { 
     if (*mTypeInfo != typeid(T*)) { 
      throw std::runtime_error("Bad type"); 
     } 
     if (mPointer == nullptr) { 
      throw std::runtime_error("nullptr was stocked"); 
     } 
     *reinterpret_cast<T*>(mPointer) = new_value; 
    } 

private: 
    void* mPointer = nullptr; 
    const std::type_info* mTypeInfo = nullptr; 
}; 

или безопаснее boost::any которые управляют сам по себе тип:

class Committer 
{ 
public: 

    template <typename T> 
    void mark_var_change(T *var) 
    { 
     mPointer = var; 
    } 

    template <typename T> 
    void commit_change(T new_value) 
    { 
     T* pointer = boost::any_cast<T*>(mPointer); // throw with bad type 
     if (pointer == nullptr) { 
      throw std::runtime_error("nullptr was stocked"); 
     } 
     *pointer = new_value; 
    } 

private: 
    boost::any mPointer; 
}; 

Live example

+0

Уже пробовал первое решение, но даже опечатка была бы фатальной в этом случае, перезаписывая последующие части структуры (если повезет) ... делает неуправляемый boost :: any exist Кстати? На данный момент boost является нежизнеспособным решением ... –

+0

@FrancisStraccia: 'void *' версия модифицирована для использования типа typeid для проверки типа. – Jarod42

Смежные вопросы