Приведение из указателя на функцию-член к другому типу и обратно, проблема строгого наложения имен?

Я написал класс для хранения указателя функции или указателя на функцию-член (не одновременно). Когда я сохраняю указатель на функцию-член, я также сохраняю указатель на объект (получатель).

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

У меня есть код, похожий на этот:

template <typename... Args>
class A
{
public:
template <typename Object, typename Ret>
A (Object *receiver, Ret (Object::*mem)(Args...)); // store the member function pointer

template <typename Ret>
A (Ret (*function)(Args...)); // store the global function pointer

void operator()(Args... args) const; // to call the function pointer
// ... more public stuff ...

private:
class UndefinedClass;
typedef void (Undefined::*MemFunPtrType)(Args...)

union
{
struct
{
MemFunPtrType memFunPtr; // the stored member function
void *receiverPtr; // the receiver object
void (*call)(void *, MemFunPtrType, Args...);
} member;

void (*funPtr)(Args...); // the stored global function
};

enum class Type {Function, Member} type;
};

Поскольку нужна только одна, глобальная функция или функция-член, я помещаю все внутри union,

В конструкторе я бросил функцию-член mem к void (Undefined::*)(Args...) и сохранить его. Я взял этот трюк из реализации std :: function.

Используя лямбду без захвата, я снова приведу к исходному типу, как объекту, так и функции, и вызываю их:

typedef Ret (Object::*OriginalMemberType)(Args...);
// ...
member.call = [] (void *receiver, MemFunPtrType memFun, Args... args)
{
(static_cast<Object*>(receiver)->*reinterpret_cast<OriginalMemberType>(memFun))(args...);
}

Я храню эту лямбду в call указатель на функцию для вызова внутри operator(), С предложением if-else я сравниваю данные типа и вызываю правильный указатель.

Я знаю, что это немного раздражает, но это работает. У меня много тестов, и все они проходят. Но я беспокоюсь о строгое наложение вещь. Я часто использую указатели, и я не уверен, что это приводит к неопределенному поведению.

Вопрос: Разрешено ли кастовать из Ret (Object::*)(Args...) в void (UndefinedClass::*)(Args...)? (Ret, объект а также Args являются аргументами шаблона)
Заметьте, я никогда не вызываю объект без приведения снова к исходному типу. Это только для хранения.

Вопрос:: Должен ли я скомпилировать с -fno-strict-aliasing? Если я должен, и sice это класс шаблона, я должен поставить -fno-strict-aliasing в каждом проекте, использующем этот класс? Может быть, я мог бы использовать массив char длины sizeof(void (UndefinedClass::*)(Args...)) или что-то в этом роде.

Заметки:

  1. Я использую gcc 4.8.1 в Archlinux. Я использую C ++ 11.
  2. Я не использовал std :: function здесь только для того, чтобы использовать заполнители с std :: bind (я не знаю, сколько требуется заполнителей). И это хорошая практика и обучение в конце концов. Если вы знаете, как это сделать с помощью std :: function, ответ очень приветствуется.
  3. На самом деле, я использую этот класс в std :: vector для вызова многих «обратных вызовов» с одной и той же сигнатурой (каркас типа сигнал / слот).

Огромное спасибо. Я уточню любой аспект этого беспорядка, если потребуется 🙂

0

Решение

Пока вы не обращаетесь к адресу с указанным неверным типом указателя. Вы не нарушаете правила. так что это не проблема. Вы просто нарушаете правило, если обращаетесь к нему с помощью неверного перфорированного указателя на другой тип, который может рассматриваться как похожий, но не тот же. Возможно, вы уже использовали сокеты FreeBSD, они выдают в функцию bind () также строгое предупреждение псевдонимов, потому что ваше приведение sockadr_in к sockadr и внутри него приведено обратно к соответствующему типу реализации. В предупреждении говорится, что это может нарушить правило, но реализация этого не позволяет.
Как хорошо описано в этом вопросе: Сокеты Беркли, нарушающие правила наложения имен?

И если вы даже не уверены в том, на что вы пойдете, вы также можете сделать приведение к char *. Потому что (начиная с c99, и я полагаю, что это должно быть одинаково в стандартах c ++), char * разрешен для псевдонимов всего Так как вы находитесь в C ++, вы даже можете обрабатывать с помощью шаблонов приведение типов к типу char и обратно внутри функции. (Хорошо, приведение типа char * не очень хорошая идея, если ваши функции предназначены не только для внутреннего использования.)

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]