VC6. template member function
От: Anton V. Kolotaev  
Дата: 19.09.02 16:16
Оценка:
Привет всем.

Это баг VC6?

struct X {
   template <class T>
       void f(T * =0) 
   {} 
};

void g(X x) 
{
   x.f<X>();   // ошибка!
   x.f(&x);    // все ok!
}
Re: VC6. template member function
От: orangy Россия
Дата: 20.09.02 04:19
Оценка:
Здравствуйте Anton V. Kolotaev, Вы писали:

AVK>Привет всем.


AVK>Это баг VC6?

фича всмысле, документированная несовместимость.
обойти можно так:
struct X {
   template <class T>
       void f(T * =0) 
   {} 
};

template<class T>
void proxy(X &x) // не нужен параметр типа шаблона 
{
  x.f((T*)0);
}

void g(X x) 
{
   x.f<X>();   // ошибка!
   proxy<X>(x); // а так нормально
   x.f(&x);    // все ok!
}


Сразу скажу, пишу по памяти, мог чего и напутать.
... << J 1.0 alpha 4 >>
"Develop with pleasure!"
Re[2]: VC6. template member function
От: Anton V. Kolotaev  
Дата: 20.09.02 07:36
Оценка: 12 (1)
Здравствуйте orangy, Вы писали:

O>фича всмысле, документированная несовместимость.

O>обойти можно так:

Жалко, но все равно спасибо.
Дело в том, пробую написать обертки для объектной модели MS Word.
Хотелось бы получить такую запись для обращние к методу ОМ:

value = object.method<Policy>(parameters);


где Policy — политика обработки исключений, нулевых указателей и HRESULT, возвращаемых из Word.
Придется писать

value = object.method(parameters,policy_object);

Re[3]: VC6. template member function
От: Кодт Россия  
Дата: 20.09.02 08:39
Оценка:
Здравствуйте Anton V. Kolotaev, Вы писали:

AVK>Жалко, но все равно спасибо.

AVK>Дело в том, пробую написать обертки для объектной модели MS Word.
AVK>Хотелось бы получить такую запись для обращние к методу ОМ:

AVK>
AVK>value = object.method<Policy>(parameters);
AVK>


AVK>где Policy — политика обработки исключений, нулевых указателей и HRESULT, возвращаемых из Word.

AVK>Придется писать

AVK>
AVK>value = object.method(parameters,policy_object);
AVK>

AVK>

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

Кстати, красивая идея — иметь варианты policy_object (а фактически, хэндлера ошибок).
Спасиба.
Перекуём баги на фичи!
Re[4]: wrapper implementation
От: Anton V. Kolotaev  
Дата: 20.09.02 11:59
Оценка:
Здравствуйте Кодт, Вы писали:


К>А по-моему, так даже лучше.


Ну, если интересно, тогда вот примерная реализация обертки


    struct View
        :   public CComPtr<wrd::View>  // можно было бы и агрегировать указатель
    {
        typedef CComPtr<wrd::View> base;

        View () : base () {}

        View (wrd::View *p)
            :   base (p)
        {}

        template <class ErrPolicy>
          word::Zoom  zoom (ErrPolicy & policy) 
        {
          word::Zoom z;                // z == 0
          if (policy.CheckPtr(p)){     // проверяем собственный указатель на 0.
                                       // можем или обругать или промолчать
            try {
              if (!policy.CheckResult(p->get_Zoom(&z)))    // проверяем HRESULT
                  z = 0;                         // если произошла ошибка, z доверять нельзя
            } catch (...) {
              policy.OnException(); z = 0;
            }
            if (!z) policy.OnNullPtr();
          }
          return z;            // можем вернуть и нулевой указатель
        }

    };


что позволяет записывать примерно следующее


    err_policies::DefaultPolicy  plcStd;

    IUnknown *pDocument =  ...;

    word::Window window = word::Document(pDocument).activeWindow (plcStd);

    window.setSplit (false, plcStd);
    window.view(plcStd).setPrintView(plcStd);
    window.view(plcStd).zoom (plcStd) = 1.0;


Как видно, от политики требуется 4 метода

  struct DefaultPolicy
  {
    template <class T>
      static bool CheckPtr (T *ptr) {
        return ptr != 0;
      }

    static bool CheckResult (HRESULT hr) {
      HR_ASSERT(hr); return SUCCEEDED(hr);
    }

    static void OnException () {
      COND_ASSERT( false && "Exception has thrown." );
    }

    static void OnNullPtr () {
      COND_ASSERT( false && "Word's method has returned null pointer." );
    }
  };


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

template <class T>
  static bool CheckPtrEx(T *ptr, ...) {  return CheckPtr(ptr); }
static bool CheckResultEx(HRESULT hr, ...) { return CheckResult(hr); }
static void OnExceptionEx(...) { OnException(); }
static void OnNullPtrEx(...)  { OnNullPtr();  }


В таком случае, "продвинутый" протокол будет вызывать Ex-методы, передавая спеифичные аргументы, которых умеют разбирать специфичные политики. В стандартных политиках выполняется действия по умолчанию.

Вот такое вот частное решение проблемы об обмене информацией между объектом и его наблюдателем.
Re[5]: wrapper implementation
От: Anton V. Kolotaev  
Дата: 20.09.02 12:16
Оценка:
Здравствуйте Anton V. Kolotaev, Вы писали:

Маленький ляп


AVK>    static void OnException () {
AVK>      COND_ASSERT( false && "Exception was thrown." );
AVK>    }
Re[5]: wrapper implementation
От: Кодт Россия  
Дата: 20.09.02 12:24
Оценка:
Здравствуйте Anton V. Kolotaev, Вы писали:

AVK>Как видно, от политики требуется 4 метода


AVK>
AVK>  struct DefaultPolicy
AVK>  {
AVK>    template <class T>
AVK>      static bool CheckPtr (T *ptr) {
AVK>        return ptr != 0;
AVK>      }

AVK>    static bool CheckResult (HRESULT hr) {
AVK>      HR_ASSERT(hr); return SUCCEEDED(hr);
AVK>    }

AVK>    static void OnException () {
AVK>      COND_ASSERT( false && "Exception has thrown." );
AVK>    }

AVK>    static void OnNullPtr () {
AVK>      COND_ASSERT( false && "Word's method has returned null pointer." );
AVK>    }
AVK>  };
AVK>


(не совсем понял смысл двух методов работы с пойнтером).

AVK>Если метод какой-нибудь обертки поддерживает более "продвинутый" протокол сообщений об ошибках, то можно создать политику, которая его поддерживает. В принципе можно использовать для обработки "продвинутого" протокола и стандартные политики — для этого надо определять еще четыре метода


AVK>
AVK>template <class T>
AVK>  static bool CheckPtrEx(T *ptr, ...) {  return CheckPtr(ptr); }
AVK>static bool CheckResultEx(HRESULT hr, ...) { return CheckResult(hr); }
AVK>static void OnExceptionEx(...) { OnException(); }
AVK>static void OnNullPtrEx(...)  { OnNullPtr();  }
AVK>


AVK>В таком случае, "продвинутый" протокол будет вызывать Ex-методы, передавая спеифичные аргументы, которых умеют разбирать специфичные политики. В стандартных политиках выполняется действия по умолчанию.


AVK>Вот такое вот частное решение проблемы об обмене информацией между объектом и его наблюдателем.


Кстати, необязательно делать политикана шаблоном.
Можно через виртуальные функции.
interface IErrorPolicy
{
  virtual bool CheckPtr(const void* p) = 0; // true if allowed to continue
  virtual bool CheckHResult(HRESULT hr) = 0;
  virtual bool CheckException() = 0; // true - continue, false - throw
};

class DefaultPolicy : public IErrorPolicy
{
  bool CheckPtr(const void* p) { ASSERT(p && "Null pointer"); return p != NULL; }
  bool CheckHResult(HRESULT hr) { bool ok = SUCCEEDED(hr); ASSERT(ok && "Failure code"); return ok; }
  bool CheckException() { ASSERT(false && "Exception"); return true; }
};

class PanicPolicy : public IErrorPolicy
{
  bool CheckPtr(const void* p) { if(!p) throw PanicException("Null pointer"); return true; }
  bool CheckHResult(HRESULT hr) { if(FAILED(hr)) throw PanicException("Failure code"); return true; }
  bool CheckException() { throw PanicException("Exception"); return false; }
};


Еще немного помозговать — и до Q&A дойдем.
Перекуём баги на фичи!
Re[6]: wrapper implementation
От: Anton V. Kolotaev  
Дата: 20.09.02 13:17
Оценка:
Здравствуйте Кодт, Вы писали:

К>(не совсем понял смысл двух методов работы с пойнтером).


Разница в том, что CheckPtr проверяет значение указателя, по которому совершается вызов метода, а OnNullPtr содержит реакцию на возврат этим методом нулевого указателя.

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

К>Кстати, необязательно делать политикана шаблоном.

К>Можно через виртуальные функции.

Вот в том то и вся изюминка!

Специфичная политика может определить обработчик, который принимает больше информации.
Напрмер, можно определить разные обработчики для семейств исключений

struct SpecificPolicy : DefaultPolicy
{
static void OnExceptionEx(ExceptionKindA e);
static void OnExceptionEx(ExceptionKindB e);
};

// кстати, с троеточием я еще толком не работал, так что этот вариант может и не работать.

Для обертки, которая вызывает OnExceptionEx с параметром при использовании SpecificPolicy будут вызваны специализированные обработчики. С другой стороны, если использовать стандартную политику, то будет вызываться метод по умолчанию OnExceptionEx(...). Заметь, это несколько отличается от предлагаемого тобой варианта: у тебя обертка будет выражена в терминах интерфейса политики — чем специфичней протокол, тем более сложный будет интерфейс, и вместо специализированного обработчика нельзя будет использовать DefaultPolicy. В моем варианте такая совместимость сохранена.

Теперь продолжение реализации.
Политика должна определить как минимум четыре метода. Каждый из них может или промолчать, или выругаться, либо сделать что-нибудь еще. Чтобы упростить создание оберток, которые комбинируют разные стратегии (например, нулевой указатель и FAILED(hr) игнорируем, а на исключение ругаемся), можно использовать такой "генератор".

namespace err_policies
{
  struct Silent
  {
    struct CCheckPtr {
      template <class T>
        static bool CheckPtr (T *ptr) {
          return ptr != 0;
        }
    };
    struct CCheckResult {
      static bool CheckResult (HRESULT hr) {
        return SUCCEEDED(hr);
      }
    };

    struct COnException {
      static void OnException () {}
    };

    struct COnNullPtr {
      static void OnNullPtr () {}
    };
  };

  struct MsgBox
  {
    struct CCheckPtr {
      template <class T>
        static bool CheckPtr (T *ptr) {
          COND_ASSERT( ptr && "Method's call via null pointer." );
        }
    };
    struct CCheckResult {
      static bool CheckResult (HRESULT hr) {
        HR_ASSERT(hr); return SUCCEEDED(hr);
      }
    };

    struct COnException {
      static void OnException () {
        COND_ASSERT( false && "An exception was thrown." );
      }
    };

    struct COnNullPtr {
      static void OnNullPtr () {
        COND_ASSERT( false && "Word's method has returned null pointer." );
      }
    };
  };

  template 
    <
      class CheckPtrSpace, 
      class CheckResultSpace, 
      class OnExceptionSpace, 
      class OnNullPointerSpace
    >
    struct PolicyGenerator
  {
    struct RET 
      :      CheckPtrSpace::CCheckPtr
      ,   CheckResultSpace::CCheckResult
      ,   OnExceptionSpace::COnException
      , OnNullPointerSpace::COnNullPtr
    {};      
  };

  typedef PolicyGenerator<Silent,MsgBox,MsgBox,MsgBox>::RET  DefaultPolicy;
  typedef PolicyGenerator<MsgBox,MsgBox,MsgBox,MsgBox>::RET  StrictPolicy;
  typedef PolicyGenerator<Silent,Silent,MsgBox,Silent>::RET  TolerantPolicy;

}


ЗЫ. Без RET можно и обойтись, однако так принято...
Re[7]: wrapper implementation
От: Кодт Россия  
Дата: 20.09.02 14:24
Оценка: 10 (1)
Здравствуйте Anton V. Kolotaev, Вы писали:

AVK>Здравствуйте Кодт, Вы писали:


К>>Кстати, необязательно делать политикана шаблоном.

К>>Можно через виртуальные функции.

AVK>Вот в том то и вся изюминка!


AVK>Специфичная политика может определить обработчик, который принимает больше информации.

AVK>Напрмер, можно определить разные обработчики для семейств исключений

AVK>struct SpecificPolicy : DefaultPolicy
AVK>{
AVK>   static void OnExceptionEx(ExceptionKindA e);
AVK>   static void OnExceptionEx(ExceptionKindB e);
AVK>};


AVK>// кстати, с троеточием я еще толком не работал, так что этот вариант может и не работать.


AVK>Для обертки, которая вызывает OnExceptionEx с параметром при использовании SpecificPolicy будут вызваны специализированные обработчики. С другой стороны, если использовать стандартную политику, то будет вызываться метод по умолчанию OnExceptionEx(...). Заметь, это несколько отличается от предлагаемого тобой варианта: у тебя обертка будет выражена в терминах интерфейса политики — чем специфичней протокол, тем более сложный будет интерфейс, и вместо специализированного обработчика нельзя будет использовать DefaultPolicy. В моем варианте такая совместимость сохранена.


К политике предъявляются требования по интерфейсу: иметь заданный набор функций с более-менее определенными сигнатурами.
Как только клиент (т.е. метод, использующий политику) потребует расширения интерфейса, то старые классы — неважно, шаблон это или класс с виртуальными методами — уже не подойдут.

В случае шаблонной реализации — чуть больше возможности для маневра: можно избегать строгой типизации аргументов — пусть компилятор плодит специализации по необходимости. Но число аргументов все равно сохранится.

(Возможно, троеточие — это выход; тогда DefaultPolicy обязан во всех своих методах держать это троеточие).

Только что проверил. VC6 работает:
class CheckNotNull
{
public:
  template<class V>
  bool operator() (V ptr, ...) { return ptr != NULL; }
};

class CheckExact
{
public:
  template<class A, class B>
  bool operator() (A value, B match, ...) { return value == match; }
};

class CheckAndRemember
{
public:
  std::string diagnostics;

  template<class A, class B, class D>
  bool operator(A value, B match = B(), D d = 0, ...)
  {
    if(value == match)
      return true;
    diagnostics = d;
    return false;
  }
};

// Класс с шаблонными методами не компилится почему-то.
// Если сделать сам класс шаблонным - то все окей.
// Я сейчас ошибку искать не буду - оставлю As-Is - смысл в общем понятен.
class Examinee
{
public:
  template<class Check>
  int Method1(Check* = 0)
  {
    if(Check()("hello", "hello", "dummy"))
      std::cout << "passed\n";
    else
      std::cout << "failed\n";
  }

  template<class Check>
  int Method2(Check& check) // допускается передача внешнего экземпляра политики
  {
    if(check("hello", "hello", "dummy"))
      std::cout << "passed\n";
    else
      std::cout << "failed\n";
  }
};

Examinee e;

e.Method1<CheckNotNull>();
e.Method1<CheckExact>();

CheckAndRemember cam;
e.Method2<CheckNotNull>();
e.Method2(cam);
std::cout << cam.diagnostics << endl;



AVK>Теперь продолжение реализации.

AVK>Политика должна определить как минимум четыре метода. Каждый из них может или промолчать, или выругаться, либо сделать что-нибудь еще. Чтобы упростить создание оберток, которые комбинируют разные стратегии (например, нулевой указатель и FAILED(hr) игнорируем, а на исключение ругаемся), можно использовать такой "генератор".

Я тащусс.
Пойду обогашать идеями народ в конторе.

<skipped>

AVK>  template 
AVK>    <
AVK>      class CheckPtrSpace, 
AVK>      class CheckResultSpace, 
AVK>      class OnExceptionSpace, 
AVK>      class OnNullPointerSpace
AVK>    >
AVK>    struct PolicyGenerator
AVK>  {
AVK>    struct RET 
AVK>      :      CheckPtrSpace::CCheckPtr
AVK>      ,   CheckResultSpace::CCheckResult
AVK>      ,   OnExceptionSpace::COnException
AVK>      , OnNullPointerSpace::COnNullPtr
AVK>    {};      
AVK>  };

AVK>  typedef PolicyGenerator<Silent,MsgBox,MsgBox,MsgBox>::RET  DefaultPolicy;
AVK>  typedef PolicyGenerator<MsgBox,MsgBox,MsgBox,MsgBox>::RET  StrictPolicy;
AVK>  typedef PolicyGenerator<Silent,Silent,MsgBox,Silent>::RET  TolerantPolicy;

AVK>}


AVK>ЗЫ. Без RET можно и обойтись, однако так принято...

Я так понимаю, чтобы было что специализировать?
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.