Получение параметров в функции типа test(int k,...)
вариантах:
test(1,1,1,1);
test(2,"aaaa",1);
test(3,true,12.5);
Подскажите, как получить передаваемые параметры в функцию.
И как определить, какого типа передаются данные?
А может будут другие варианты?
Допустим функция типа void test(int k, ...) вызывается в различных
вариантах:
test(1,1,1,1);
test(2,"aaaa",1);
test(3,true,12.5);
Подскажите, как получить передаваемые параметры в функцию.
И как определить, какого типа передаются данные?
А может будут другие варианты?
Учимся программировать на С++ ПРАВИЛЬНО.
Вариант первый,- полиморфизм.
Определи несколько функций с одним именем и разным набором параметров.
void test(int k, const char*, int);
void test(int k, bool, float);
Вариант второй, частный случай первого,- шаблонны.
Определи шаблонную функцию и при необходимости её частные специализации.
void test(TArg1 k, TArg2, TArg3, TArg4);
// частная специализация
template <>
void test(int k, int, int, int);
Вариант третий, продвинутый,- интеграция параметров.
Параметры обычно логически группируются в структуры, напрмер, координаты x,y,z логично объединить в структуру position.
Вариант четвертый, супер продвинутый,-инкапсуляция данных с представлением единого интерфейса.
Объединив несколько аргументов в структуру, мы можем их инкапсулировать и предоставить единый интерфейс, который уже сам будет решать как представлять данные для внешнего пользователя.
На следующем шаге, окажется, что твоя функция не нужна вовсе, т.к. она переродится в один или несколько методов класса, инкапсулирующего данные.
Учимся программировать на С++ ПРАВИЛЬНО.
Вариант первый,- полиморфизм.
Определи несколько функций с одним именем и разным набором параметров.
void test(int k, const char*, int);
void test(int k, bool, float);
Вариант второй, частный случай первого,- шаблонны.
Определи шаблонную функцию и при необходимости её частные специализации.
void test(TArg1 k, TArg2, TArg3, TArg4);
// частная специализация
template <>
void test(int k, int, int, int);
Вариант третий, продвинутый,- интеграция параметров.
Параметры обычно логически группируются в структуры, напрмер, координаты x,y,z логично объединить в структуру position.
Вариант четвертый, супер продвинутый,-инкапсуляция данных с представлением единого интерфейса.
Объединив несколько аргументов в структуру, мы можем их инкапсулировать и предоставить единый интерфейс, который уже сам будет решать как представлять данные для внешнего пользователя.
На следующем шаге, окажется, что твоя функция не нужна вовсе, т.к. она переродится в один или несколько методов класса, инкапсулирующего данные.
Мне нужно что-то типа функции printf() !
Мне нужно что-то типа функции printf() !
А зачем?
Ты на С или на С++ пишешь?
Если на С++, то забывай про (...), это НЕПРАВИЛЬНЫЙ подход, без которого можно весьма неплохо жить.
CExceptionMsg::CExceptionMsg(const char*mes,...)
{
char buffer[100];
va_list marker;
va_start(marker,mes);
vsprintf(&buffer[0],mes, marker);
va_end(marker);
fmessage=(char*)malloc(strlen(buffer)+1);
strcpy(fmessage, buffer);
}
У меня такой код для произвольного числа параметров
CExceptionMsg::CExceptionMsg(const char*mes,...)
{
char buffer[100];
va_list marker;
va_start(marker,mes);
vsprintf(&buffer[0],mes, marker);
va_end(marker);
fmessage=(char*)malloc(strlen(buffer)+1);
strcpy(fmessage, buffer);
}
Хм... Код прямо скажем небезопасный... это из-за массива.
Кроме того некоторые места совсем непонятные, например, зачем это &buffer[0], вполне обощлось бы и просто buffer. Зачем строка потом копируется?
Что же касается переменного числа параметров, я бы советовал отказаться от них.
Что же касается типа передаваемых аргументов, то определить их НЕ ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ. Это и есть то самое пагубное влияние void* Единственный способ передать информацию о типах - это передавать информацию о них одним из явных параметров (например первым) в виде строки, которую потом придется парсить (а-ля printf), либо каким-то хитрым ID. Но в любом случае это небезопасно, и неудобно.
А зачем?
Ты на С или на С++ пишешь?
Если на С++, то забывай про (...), это НЕПРАВИЛЬНЫЙ подход, без которого можно весьма неплохо жить.
Хоть я никогда и не юзал (...), а всегда использывал вместо этого массивы, все же хочеться узнать почему надо избегать (...) ?
Хм... Код прямо скажем небезопасный... это из-за массива.
Кроме того некоторые места совсем непонятные, например, зачем это &buffer[0], вполне обощлось бы и просто buffer. Зачем строка потом копируется?
Долго не мог определеить, как же можно в рамках идеологии MFC создать объект исключение, который бы сохранял простое сообщение для пользователя и мог бы выводить его на экран в блоке try-catch.
Получился следующий класс:
{
DECLARE_DYNAMIC(CExceptionMsg)
private:
char* fmessage;
public:
CExceptionMsg(char*mes);
CExceptionMsg(const char*mes, ...);
virtual ~CExceptionMsg();
virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
PUINT pnHelpContext = NULL);
};
И его реализация
CExceptionMsg::CExceptionMsg(char*mes)
{
fmessage=(char*)malloc(strlen(mes)+1);
strcpy(fmessage, mes);
}
CExceptionMsg::CExceptionMsg(const char*mes,...)
{
char buffer[100];
va_list marker;
va_start(marker,mes);
vsprintf(buffer,mes, marker);
va_end(marker);
fmessage=(char*)malloc(strlen(buffer)+1);
strcpy(fmessage, buffer);
}
CExceptionMsg::~CExceptionMsg()
{
free(fmessage);
}
BOOL CExceptionMsg::GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
PUINT pnHelpContext)
{
ASSERT(lpszError != NULL && AfxIsValidString(lpszError, nMaxError));
lstrcpyn(lpszError, fmessage, nMaxError);
return TRUE;
}
Для объекта этого класса можно вызывать MFC-методы, например Delete, ReportError
Хоть я никогда и не юзал (...), а всегда использывал вместо этого массивы, все же хочеться узнать почему надо избегать (...) ?
Нарушение типовой безопасности.
Три точки - это тоже самое, что void*.
Приводя что-либо к void*, ты теряешь информацию о типах, отсюда возможны различные критические ошибки.
Долго не мог определеить, как же можно в рамках идеологии MFC создать объект исключение, который бы сохранял простое сообщение для пользователя и мог бы выводить его на экран в блоке try-catch.
Получился следующий класс:
{
DECLARE_DYNAMIC(CExceptionMsg)
private:
char* fmessage;
public:
CExceptionMsg(char*mes);
CExceptionMsg(const char*mes, ...);
virtual ~CExceptionMsg();
virtual BOOL GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
PUINT pnHelpContext = NULL);
};
И его реализация
CExceptionMsg::CExceptionMsg(char*mes)
{
fmessage=(char*)malloc(strlen(mes)+1);
strcpy(fmessage, mes);
}
CExceptionMsg::CExceptionMsg(const char*mes,...)
{
char buffer[100];
va_list marker;
va_start(marker,mes);
vsprintf(buffer,mes, marker);
va_end(marker);
fmessage=(char*)malloc(strlen(buffer)+1);
strcpy(fmessage, buffer);
}
CExceptionMsg::~CExceptionMsg()
{
free(fmessage);
}
BOOL CExceptionMsg::GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
PUINT pnHelpContext)
{
ASSERT(lpszError != NULL && AfxIsValidString(lpszError, nMaxError));
lstrcpyn(lpszError, fmessage, nMaxError);
return TRUE;
}
Для объекта этого класса можно вызывать MFC-методы, например Delete, ReportError
Проблема не в классе исклучения, а в том, как ты передаешь параметры.
Как я понимаю, ты в курсе, где и какие исключения могут возникнуть. Почему бы тогда не создать методы (точнее конструкторы), принимающие конкретные параметры?
Т.е. что-то типа такого:
CExceptionMsg::CExceptionMsg(char*mes, int code, bool result);
CExceptionMsg::CExceptionMsg(char*mes, int* array);
// и т.п.
Если же таких конструкторов получается много, но количество параметров примерно одинаково и они стандартных типов, то почему бы не воспользоваться шаблонами:
CExceptionMsg::CExceptionMsg(T arg1, U arg2)
{
std::stringstream message;
message << "ERROR: " << arg1 << arg2;
}
Если же сообщения об ошибках вообще непредсказуемы (что-то я сомневаюсь) или их разнообразие конкурирует с разнообразием снежинок, то можно конструировать сообщение непосредственно перед вызовом throw:
// Где-то во Вселенной программного кода...
std::stringstream message;
message << "ERROR: " << arg1 << arg2;
throw message.str();
А зачем?
Ты на С или на С++ пишешь?
Если на С++, то забывай про (...), это НЕПРАВИЛЬНЫЙ подход, без которого можно весьма неплохо жить.
У меня несколько DLL используют одну и туже функцию родителя.
А решение я сам себе подсказал: printf().
{
int *piInt = NULL, **pIInt=NULL;
CStringArray csAr;
bool *pbBool = NULL;
int kiInt=0, kIInt=0, kbBool=0;
if(Param){
int L = (int)strlen(Param);
if(L>0){
for(int a=0; a<L; a++){
switch(Param[a]){
case 'i': kiInt++; break;
case 'I': kIInt++; break;
case 'b': kbBool++; break;
}
}
if(kiInt) piInt = new int[kiInt];
if(kIInt) pIInt = new int*[kIInt];
if(kbBool) pbBool = new bool[kbBool];
va_list arg_ptr;
va_start(arg_ptr, Param);
kiInt = 0; kIInt = 0; kbBool = 0;
for(int k=0;k<L;k++) {
switch(Param[k]){
case 'i': piInt[kiInt] = va_arg(arg_ptr,int); kiInt++; break;
case 'I': pIInt[kIInt] = va_arg(arg_ptr,int*); kIInt++; break;
case 'b': pbBool[kbBool] = va_arg(arg_ptr,bool); kbBool++; break;
case 's': csAr.Add(va_arg(arg_ptr,char*)); break;
}
}
va_end(arg_ptr);
}
}
//--------------------
а тут делаем что нужно ...
}
// освобождаем память
if(piInt) delete []piInt;
if(pIInt) delete []pIInt;
if(pbBool) delete []pbBool;
csAr.RemoveAll();
}
Вызываем функцию так:
test(1,"is",10,"aaaaa");
И нет проблем ни с типами, ни с их количеством...
Спасибо всем!
{
int *piInt = NULL, **pIInt=NULL;
CStringArray csAr;
bool *pbBool = NULL;
int kiInt=0, kIInt=0, kbBool=0;
if(Param){
int L = (int)strlen(Param);
if(L>0){
for(int a=0; a<L; a++){
switch(Param[a]){
case 'i': kiInt++; break;
case 'I': kIInt++; break;
case 'b': kbBool++; break;
}
}
if(kiInt) piInt = new int[kiInt];
if(kIInt) pIInt = new int*[kIInt];
if(kbBool) pbBool = new bool[kbBool];
va_list arg_ptr;
va_start(arg_ptr, Param);
kiInt = 0; kIInt = 0; kbBool = 0;
for(int k=0;k<L;k++) {
switch(Param[k]){
case 'i': piInt[kiInt] = va_arg(arg_ptr,int); kiInt++; break;
case 'I': pIInt[kIInt] = va_arg(arg_ptr,int*); kIInt++; break;
case 'b': pbBool[kbBool] = va_arg(arg_ptr,bool); kbBool++; break;
case 's': csAr.Add(va_arg(arg_ptr,char*)); break;
}
}
va_end(arg_ptr);
}
}
//--------------------
а тут делаем что нужно ...
}
// освобождаем память
if(piInt) delete []piInt;
if(pIInt) delete []pIInt;
if(pbBool) delete []pbBool;
csAr.RemoveAll();
}
ты уж не обижайся, но это ужас...... практически нечитабельно....одноразовый код... :)
ты уж не обижайся, но это ужас...... практически нечитабельно....одноразовый код... :)
А я и не обижаюсь, т.к.:
1. Ограничен во времени
2. Никто подходящего варианта не предложил
3. Больше ничего в голову не пришло, а этот одноразовый (именно одноразовый) ужас работает!
4. Выложил этот код как вариант, идею...
У меня несколько DLL используют одну и туже функцию родителя.
Гмс...если не секрет конечно, поделитесь пожалуйста почему несколько DLL(что бы под этим не подразумевалось) не могут использовать разные функции родителя ? и не пришлось бы писать этот код ( другими словами он лишний ).
А я и не обижаюсь, т.к.:
1. Ограничен во времени
2. Никто подходящего варианта не предложил
3. Больше ничего в голову не пришло, а этот одноразовый (именно одноразовый) ужас работает!
4. Выложил этот код как вариант, идею...
Могу согласиться с этим только если это какая та лаба или курсовая или даже диплом :).
Гмс...если не секрет конечно, поделитесь пожалуйста почему несколько DLL(что бы под этим не подразумевалось) не могут использовать разные функции родителя ? и не пришлось бы писать этот код ( другими словами он лишний ).
В общих чертах где-то так:
Прога по старту ищет свои DLL и добавляет в меню пункты. И чтобы не морочить голову с кучей функций по передаче параметров (для каждой DLL - свои), вызовов кучи функций родителя, пришлось так поступить. В каждой DLL только 4 функции: инфо о DLL, старт, вызов функции родителя, стоп. Теперь я только передаю клиенту очередную DLL.
В общих чертах где-то так:
Прога по старту ищет свои DLL и добавляет в меню пункты. И чтобы не морочить голову с кучей функций по передаче параметров (для каждой DLL - свои), вызовов кучи функций родителя, пришлось так поступить. В каждой DLL только 4 функции: инфо о DLL, старт, вызов функции родителя, стоп. Теперь я только передаю клиенту очередную DLL.
ты че фотошоп свой пишешь? или это снимки из космоса ?...эх ностальгия по родному КБ,
ладно....эт называется callback-фунция .....
предлагаю следующее решение :
class Info
{
public:
virtual CString GetInfoMessage();
};
class SomeInfo : public Info
{.....
};
callback-функция : test( Info* );
дальше объяснять ?
дальше объяснять ?
Буду очень признателен за разъяснения!
Замучился между тремя соснами путаться...
Буду очень признателен за разъяснения!
Замучился между тремя соснами путаться...
насколько я въехал тебе на передать через функцию родителя ( она же callback-функция ) толи какую то инфу то ли сообщение об ошибке.. причем если это инфа то она может быть весьма разнообразной по типу...
идея заключалась в том что бы через общий единый интерфейс(набор методов класса возращающих какую либо инфу какого либо типа) класса Info, нессколько раз переписанный в производных от него классах ,получать эту информацию в родителе, или на худой конец использовать динамическое определние типа на этапе выполнения...но это уже не гоод....
ладно счас подожди полчаса я те код напишу.....
{
CString virtual GetInfoMessage() = 0; // эт назыается чисто виртуальная функция....
};
// callback функция( в родителе ).
test ( Info* _Info )
{
MessageBox( _Info->GetInfoMessage) ); //дык....
delete _Info;
}
// dll 1
class Dll1Info : public Info
{
CString virtual GetInfoMessage()
{
return "Dll1Info error";
}
}
где нибудь в этой же dll где вызывается callback-функция :
Info* dll1info = new Dll1Info();
test( dll1info );
// dll 2
class Dll2Info : public Info
{
CString virtual GetInfoMessage()
{
return "Dll2Info mega error";
}
}
где нибудь в этой же dll где вызывается callback-функция :
Info* dll2info = new Dll2Info();
test( dll2info );
// dll3 .....
Проблема не в классе исклучения, а в том, как ты передаешь параметры.
Как я понимаю, ты в курсе, где и какие исключения могут возникнуть. Почему бы тогда не создать методы (точнее конструкторы), принимающие конкретные параметры?
Т.е. что-то типа такого:
CExceptionMsg::CExceptionMsg(char*mes, int code, bool result);
CExceptionMsg::CExceptionMsg(char*mes, int* array);
// и т.п.
Если же таких конструкторов получается много, но количество параметров примерно одинаково и они стандартных типов, то почему бы не воспользоваться шаблонами:
CExceptionMsg::CExceptionMsg(T arg1, U arg2)
{
std::stringstream message;
message << "ERROR: " << arg1 << arg2;
}
Если же сообщения об ошибках вообще непредсказуемы (что-то я сомневаюсь) или их разнообразие конкурирует с разнообразием снежинок, то можно конструировать сообщение непосредственно перед вызовом throw:
// Где-то во Вселенной программного кода...
std::stringstream message;
message << "ERROR: " << arg1 << arg2;
throw message.str();
Согласен, но мне просто хотелось донести до пользователя отформатированное сообщение (возможно с какими-то параметрами). Так как работал с Дельфи, в нем просто использовался класс TException, который как раз это и делает. От TException в Дельфи наследуются все остальные классы исключений. В MFC Си++ идеология другая, здесь есть базовый класс CException, но он не позволяет передавать и хранить произвольные сообщения, чтобы затем передать их пользователю (в отличие от TException в Дельфи). Поэтому пришлось пойти на создание класса CExceptionMsg, чтобы с одной стороны дать возможность передать пользователю сообщение, с другой - сохранить те методы исключений, которые использует MFC.
Кстати, у Подбельского в книге "Язык Си++" описывается подобный класс xmsg (файл except.h), но он входит в библиотеку классов вместе c Borland C++.
Огромное спасибо!!!
Попутно появился вопрос по диалогу выбора каталога.
Нашел что-то похожее. Работает, но вот предварительно установить каталог не получается:
{
LPMALLOC pMalloc = NULL;
LPITEMIDLIST pidl = NULL;
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(BROWSEINFO));
TCHAR szInitialPath[MAX_PATH*2];
_tcsncpy(szInitialPath, Path->GetBuffer(), sizeof(szInitialPath)/sizeof(TCHAR)-2);
bi.hwndOwner = hwndOwner;
bi.lpszTitle = _T("Пожайлуста, выберите каталог...");
bi.lParam = (LPARAM)szInitialPath;
bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON;
pidl = SHBrowseForFolder(&bi);
if(pidl != NULL) {
SHGetPathFromIDList(pidl, Path->GetBuffer(Path->GetLength()));
// освобождаем память
if(SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc){
pMalloc->Free(pidl);
pMalloc->Release();
}
return true;
}
return false;
}
Подскажите, пожайлуста, как сие исправить?
Или может есть другие варианты выбора каталога, но обязательно чтобы можно было бы установить каталог по умолчанию.
Все получилось!!!
Огромное спасибо!!!
Попутно появился вопрос по диалогу выбора каталога.
Нашел что-то похожее. Работает, но вот предварительно установить каталог не получается:
{
LPMALLOC pMalloc = NULL;
LPITEMIDLIST pidl = NULL;
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(BROWSEINFO));
TCHAR szInitialPath[MAX_PATH*2];
_tcsncpy(szInitialPath, Path->GetBuffer(), sizeof(szInitialPath)/sizeof(TCHAR)-2);
bi.hwndOwner = hwndOwner;
bi.lpszTitle = _T("Пожайлуста, выберите каталог...");
bi.lParam = (LPARAM)szInitialPath;
bi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON;
pidl = SHBrowseForFolder(&bi);
if(pidl != NULL) {
SHGetPathFromIDList(pidl, Path->GetBuffer(Path->GetLength()));
// освобождаем память
if(SUCCEEDED(SHGetMalloc(&pMalloc)) && pMalloc){
pMalloc->Free(pidl);
pMalloc->Release();
}
return true;
}
return false;
}
Подскажите, пожайлуста, как сие исправить?
Или может есть другие варианты выбора каталога, но обязательно чтобы можно было бы установить каталог по умолчанию.
_tcsncpy(szInitialPath, Path->GetBuffer(), sizeof(szInitialPath)/sizeof(TCHAR)-2);
помоему ты че не то намудил в этой строке..сделай это как нить проще...
например :
правда это уже в другой строке...
_tcsncpy(szInitialPath, Path->GetBuffer(), sizeof(szInitialPath)/sizeof(TCHAR)-2);
помоему ты че не то намудил в этой строке..сделай это как нить проще...
например :
правда это уже в другой строке...
Исправил, но бестолку: всеравно при вызове переходит в каталог "Мой компьютер"