2009-09-17 4 views
2

Из любопытства я подумал, что попробую написать базовый класс C++, который имитирует множественный шаблон делегата C#. Кодекс ниже в основном выполняет эту работу, при этом неприятная жертва потеряет почти все виды безопасности, но при использовании исходного параметра фиктивной настройки для настройки va_list действительно кажется немного выключенным. Есть ли способ использовать va_list без этого? Я действительно понимаю, что есть способы сделать это с помощью (например) повышения, но я стремился к чему-то мертвому простому, который использовал только стандартную библиотеку.Функция Variadic без указанного первого параметра?

#include <vector> 
#include <iostream> 
#include <string> 
#include <stdarg.h> 
#include <algorithm> 

using namespace std; 

class CDelegate 
{ 
public: 
virtual bool operator()(va_list params) = 0; 
}; 

class CMultipleDelegateCaller 
{ 
public: 
typedef vector<CDelegate*> CDelegateVector; 

CMultipleDelegateCaller& operator+=(CDelegate &rDelegate) 
{ 
    m_apDelegates.push_back(&rDelegate); 
    return (*this); 
} 

CMultipleDelegateCaller& operator-=(CDelegate &rDelegate) 
{ 
    CDelegateVector::iterator iter = 
    find(m_apDelegates.begin(), m_apDelegates.end(), &rDelegate); 
    if (m_apDelegates.end() != iter) m_apDelegates.erase(iter); 
    return (*this); 
} 

bool Call(int iDummy, ...) 
{ 
    va_list params; 
    CDelegate* pDelegate; 
    CDelegateVector::iterator iter; 
    for (iter = m_apDelegates.begin(); iter != m_apDelegates.end(); ++iter) 
    { 
    pDelegate = *iter; 
    va_start(params, iDummy); 
    if (!(*pDelegate)(params)) return false; 
    va_end(params); 
    } 
    return true; 
} 

private: 
CDelegateVector m_apDelegates; 
}; 

class CTestDelegate: 
public CDelegate 
{ 
public: 
CTestDelegate():m_iId(++s_iCount) {} 
virtual bool operator()(va_list params) 
{ 
    int iIntParam = va_arg(params, int); 
    char* szCharPtrParam = va_arg(params, char*); 
    string* psStringParam = va_arg(params, string*); 
    cout<<m_iId<<"{" 
    <<iIntParam<<", " 
    <<szCharPtrParam<<", " 
    <<*psStringParam<<"}"<<endl; 

    return true; 
} 
int m_iId; 
static int s_iCount; 
}; 
int CTestDelegate::s_iCount = 0; 

int main(int argc, char* argv[]) 
{ 
CMultipleDelegateCaller cDelegateCaller; 
CTestDelegate cTestDelegate1; 
CTestDelegate cTestDelegate2; 

cout<<"--------------------"<<endl; 
cDelegateCaller += cTestDelegate1; 
cDelegateCaller += cTestDelegate2; 
string sString("World"); 
cDelegateCaller.Call(1, 2, "Hello", &sString); 
cout<<"--------------------"<<endl; 
cDelegateCaller -= cTestDelegate1; 
cDelegateCaller.Call(1, 2, "Hello", &sString); 
cout<<"--------------------"<<endl; 
cDelegateCaller -= cTestDelegate2; 
cDelegateCaller.Call(1, 2, "Hello", &sString); 
cout<<"--------------------"<<endl; 

cin>>sString; 
return 0; 
} 

ответ

4

Функции с многоточием в C++ только для совместимости с C. Использование C++ я бы вернуть временный объект помощник в Call функции и добавить шаблон operator% передать переменное количество аргументов. Для того, чтобы использовать его следующий образ:

cDelegateCaller.Call() % 2 % "Hello" % sString; // dummy argument isn't required 

Что касается вашего вопроса, стандарт требует, чтобы вызвать va_start перед любым доступом к неназванным аргументам. И va_start требует второго аргумента, который является идентификатором самого правого параметра в списке переменных параметров в определении функции.

+0

Как это можно было бы использовать для вызова функции? Вызов будет выполняться перед каждым из операторов модуля, поэтому параметры еще не были переданы. – FlintZA

+0

Фактическое действие будет выполняться в деструкторе временного объекта, возвращаемого функцией 'Call'. Вы можете найти здесь образец - http://stackoverflow.com/questions/1394053/how-to-write-a-generic-alert-message-using-win32/1394069#1394069 –

+0

Итак, ссылаясь на вышеуказанный вопрос, и один предоставлен xtofl, у меня есть внутренний помощник, который будет возвращен вызовом, и имеет оператор%, который добавляет аргументы во внутренний список arglist (как определено xtofl (http://stackoverflow.com/questions/1350657/variable-parameter -function-how-to-make-it-type-safe-and-more-importantful/1361620 # 1361620). В своем деструкторе он выполняет итерацию по списку делегатов CDelegateCaller, но как эти аргументы передаются делегатскому оператору() в таким образом, что пользователю не нужно реализовывать несколько рекурсивных функций? – FlintZA

0

Из Kirill's answer вы можете заключить, что можно создать делегат с безопасным типом, используя функцию объединения аргументов шаблона. Эта функция также нуждается в исходной отправной точке, но имеет преимущество безопасности типа.

Библиотека FastFormat использует это, boost использует это, и я однажды предоставил другой пример в answer на другой вопрос.

+0

Спасибо, я посмотрел ваши шаблоны ArgList, посмотрю комментарии на ответ Кирилла .. – FlintZA

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

  • Нет связанных вопросов^_^