Тоесть тут не будет у него ни UB ни исключения, а обе Foo получат что хотели. Я тут вижу одну нерешаемую проблему с безвозвратной утерей сигнатуры функции. Соответственно вызов оператора компилятору просто не сгенерироть.
Здравствуйте, glap, Вы писали:
G>Один товарищ утверждает, что написал делегат который абстрагируется от сигнатуры функции и способен переварить нормально такой код:
Ну, это дело нехитрое. Ниже покажу, как.
G>Тоесть тут не будет у него ни UB ни исключения, а обе Foo получат что хотели.
А что хотели обе Foo? Могу предположить, что они хотели значение по умолчанию.
G> Я тут вижу одну нерешаемую проблему с безвозвратной утерей сигнатуры функции. Соответственно вызов оператора компилятору просто не сгенерироть.
class Connector
{
boost::function< void(boost::any) > fun;
template<class Arg>
static void fun_impl(void(*f)(Arg), boost::any a)
{
if(Arg* v = boost::any_cast<Arg>(&a))
f(*v);
else
f(Arg()); // или любое другое предпочитаемое поведение - не вызывать, кинуть исключение, etc...
}
public:
template<class Arg>
Connector( void(*f)(Arg) ) : fun( boost::bind(fun_impl<Arg>, f, _1) ) {}
void operator()(boost::any a) const { fun(a); }
};
Здравствуйте, glap, Вы писали:
G>Тоесть тут не будет у него ни UB ни исключения, а обе Foo получат что хотели. Я тут вижу одну нерешаемую проблему с безвозвратной утерей сигнатуры функции. Соответственно вызов оператора компилятору просто не сгенерироть.
G>Я чего-то не знаю?
Ну к примеру конструктор Connector может быть шаблонным и далее создавать вспомогательный объект, тип которого зависит от сигнатуры аргумента.
Собственно всякие boost::function именно так и работают, единственно они требуют одинаковой сигнатуры оборачиваемых функций и функторов. Но в своем классе можно сделать как угодно...
К>class Connector
К>{
К> boost::function< void(boost::any) > fun;
К> template<class Arg>
К> static void fun_impl(void(*f)(Arg), boost::any a)
К> {
К> if(Arg* v = boost::any_cast<Arg>(&a))
К> f(*v);
К> else
К> f(Arg()); // или любое другое предпочитаемое поведение - не вызывать, кинуть исключение, etc...
К> }
К>public:
К> template<class Arg>
К> Connector( void(*f)(Arg) ) : fun( boost::bind(fun_impl<Arg>, f, _1) ) {}
К> void operator()(boost::any a) const { fun(a); }
К>};
К>
Ну так в моём примере это не сработает. operator int() не будет вызван.
any_cast сравнит имя типа входного параметра функции (int) и имя типа аргумента (SomeStruct) и выдаст NULL.
Здравствуйте, enji, Вы писали:
E>Собственно всякие boost::function именно так и работают, единственно они требуют одинаковой сигнатуры оборачиваемых функций и функторов. Но в своем классе можно сделать как угодно...
Речь о конкретном примере. Должен быть вызван конструктор копирования при входе в Foo2, а при входе в Foo1 должен быть вызван operator int();
Здравствуйте, glap, Вы писали: G>Один товарищ утверждает, что написал делегат который абстрагируется от сигнатуры функции и способен переварить нормально такой код: G>
G>Тоесть тут не будет у него ни UB ни исключения, а обе Foo получат что хотели. Я тут вижу одну нерешаемую проблему с безвозвратной утерей сигнатуры функции. Соответственно вызов оператора компилятору просто не сгенерироть. G>Я чего-то не знаю?
Пользуясь новшествами языка, это можно сделать не только для разных типов параметров, но и для произвольного их количества, а также для использования с лямбдами:
Но у этой реализации есть одно ограничение: типы и количество формальных и фактических параметров должны соответствовать друг другу без неявных преобразований. Несоответствие, если таковое возникает, обнаруживается во время исполнения с выбросом исключения. Это означает, что обращение к Foo1 из твоего примера закончится ошибкой, в то время как Foo2 отработает нормально. Думаю, что реализовать передачу фактических параметров с автоматическим применением неявных преобразований в рамках текущих возможностей языка вряд ли получится.
Вопрос был довольно конкретный. Я указал код. Про обычные делегаты коих у каждого по три велосипеда на каждого я знаю.
Просто товарищ убеждает, что приведённый мною код делает вызов оператора преобразования.
Здравствуйте, glap, Вы писали:
G>Один товарищ утверждает, что написал делегат который абстрагируется от сигнатуры функции и способен переварить нормально такой код: G>
Здравствуйте, Константин, Вы писали:
К>Делегат работает только с фиксированным конечным набором сигнатур, или претендует на всеобщность?
Претендует с его слов...
С конечным списком типов аргументов понятно как сделать, а вот с произвольным очевидно, что это невозможно.
Но решил себя перепроверить.
Здравствуйте, glap, Вы писали:
G>Вопрос был довольно конкретный. Я указал код. Про обычные делегаты коих у каждого по три велосипеда на каждого я знаю. G>Просто товарищ убеждает, что приведённый мною код делает вызов оператора преобразования.
Думаю, товарищ чего-то недоговаривает.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, glap, Вы писали:
G>>Один товарищ утверждает, что написал делегат который абстрагируется от сигнатуры функции и способен переварить нормально такой код:
К>Ну, это дело нехитрое. Ниже покажу, как.
G>>Тоесть тут не будет у него ни UB ни исключения, а обе Foo получат что хотели.
К>А что хотели обе Foo? Могу предположить, что они хотели значение по умолчанию.
G>> Я тут вижу одну нерешаемую проблему с безвозвратной утерей сигнатуры функции. Соответственно вызов оператора компилятору просто не сгенерироть.
К>
К>
Что то ты тут перемудрил. Восстанавливать сигнатуру конечно не надо, а просто организовать нормальный вызов Foo с аргументом.
Здравствуйте, johny5, Вы писали:
J>Что то ты тут перемудрил. Восстанавливать сигнатуру конечно не надо, а просто организовать нормальный вызов Foo с аргументом. J>
Здравствуйте, glap, Вы писали:
G>Ну если уж упрощать поступим самый джидайским методом:
Ну это не джедайство, а самообман. У jonny5, по крайней мере, было запоминание функции.
G>Только боюсь, как и твой код, это не имеет никакого отношения к озвученной проблеме.
Проблема в том, что проблема так и не была озвучена.
Можно только догадываться, что
— мы запоминаем любую функцию с сигнатурой void(T), где T произвольно
— затем вызываем с аргументом U, где U произвольно
— и если существует неявное приведение U к T, то оно будет сделано
В С++ невозможно сделать двухфазно конкретизируемый (шаблонный) код. А он нам как раз и потребуется, если мы хотим приводить всё ко всему, посредством static_cast, явного или неявного.
Если мы можем зафиксировать множество ожидаемых типов U, то в момент запоминания void(T) сразу породим для каждого ожидаемого U нужные переходники.
Если введём ряд ограничений на свойства типов T и U, — например, запрет на пользовательские операторы приведения, либо разрешение строго определённых приведений (через void*, к примеру), — то тоже сможем.
В яве дженерики или в хаскеле GADT'ы работают, потому что там бинарное представление у всех типов одинаковое: указатель на неизвестно что. В плюсах, ясное дело, такой номер в общем случае не пройдёт.
Здравствуйте, Кодт, Вы писали:
К>Проблема в том, что проблема так и не была озвучена.
Может я плохо формулирую, но всё что ты написал я знал. Велосипедов уже понаделал на подобные темы уйму.
У меня просто был вчера диалог с один "пациентом" который уверял меня, что его универсальный абстрагированный
делегат умеет правильно пройтись по всем веткам кода, который я привёл. То есть повести себя будто вызов функций идёт
напрямую с передачей SomeStruct. Убеждал он меня долго поэтому я начал сомневаться в своей адекватности, а не только в его.
Решил себя перепроверить, быть может что-то с языком произошло, что я пропустил или я просто идиот.
Я же не спросил как заставить этот код работать. Речь про "выдуманный" делегат и возможно ли его существование. Заголовок темы так и звучит.
Но видимо не стоит настолько в себе сомневаться и создавать такие темы.
Здравствуйте, Кодт, Вы писали:
К>- мы запоминаем любую функцию с сигнатурой void(T), где T произвольно К>- затем вызываем с аргументом U, где U произвольно К>- и если существует неявное приведение U к T, то оно будет сделано
К>В С++ невозможно сделать двухфазно конкретизируемый (шаблонный) код. А он нам как раз и потребуется, если мы хотим приводить всё ко всему, посредством static_cast, явного или неявного.
В этом случае можно будет один из набор T или U типов сложить в boost::variant или boost::any, как и предлагал.
Ещё можно будет использовать "virtual template". Список типов с которым будет вызываться код всё равно будет ограничен и его можно перечислить:
Здравствуйте, johny5, Вы писали:
К>>- мы запоминаем любую функцию с сигнатурой void(T), где T произвольно К>>- затем вызываем с аргументом U, где U произвольно К>>- и если существует неявное приведение U к T, то оно будет сделано
К>>В С++ невозможно сделать двухфазно конкретизируемый (шаблонный) код. А он нам как раз и потребуется, если мы хотим приводить всё ко всему, посредством static_cast, явного или неявного.
J>В этом случае можно будет один из набор T или U типов сложить в boost::variant или boost::any, как и предлагал. J>Ещё можно будет использовать "virtual template". Список типов с которым будет вызываться код всё равно будет ограничен и его можно перечислить:
Это всё работает только в том случае, когда присвоенный T совпадает с вызываемым U, либо способ приведения T к U заранее известен и проходит через точку стирания типа (dynamic_cast или boost::any — у каждого своя крутизна и свои ограничения).
А вот с пользовательским приведением такой номер не прокатит.
Про boost::variant — это то, о чём я написал выше: мы делаем заготовки для приведения всех типов из набора.
G>Убеждал он меня долго поэтому я начал сомневаться в своей адекватности, а не только в его.
Безотносительно сути проблемы — к чему схоластика? Если ты считаешь, что его код не работает на каком-то примере — скомпилируй и посмотри, чего проще то? Зачем считать ангелов на острие иглы?