Почему мне нужен "opCmp" для анонимного класса? - PullRequest
3 голосов
/ 17 марта 2012

Я не совсем уверен, как это объяснить, поэтому, пожалуйста, попросите меня разъяснить все, что не имеет смысла.У меня есть интерфейс и шаблонная функция, которая возвращает функции, которые возвращают анонимные внутренние классы на основе аргумента времени компиляции:

interface MyInterface {
    void getName();
}
MyInterface function() getMyInterfaceFactory(string name)() {
    return function() {
        return new class MyInterface {
            void getName() { //Do something involving name here }
        };
    };
}

Теперь getMyInterfaceFactory() был getMyInterface() и возвращал анонимныйобъект напрямую.Все работало нормальноКогда я добавил фабричные функции, я начал получать исключение во время запуска от Object:

object.Exception.....(102): need opCmp for class mymodule.getMyInterfaceFactory!("someargument").getMyInterfaceFactory.__funcliteral14.__anonclass13

Итак, я посмотрел на строку выброса в источнике druntime, и она выглядит как реализация opCmp по умолчанию дляОбъект просто бросает.Я не сравниваю ни заводские функции, ни MyInterface* храню фабрики в качестве значений ассоциативного массива с индексированной строкой, но opCmp не потребовался, когда я хранил анонимные классы непосредственно в этом массиве, только когда я начал хранить функции.Если я вставлю opCmp (используя адрес памяти), все, кажется, будет работать нормально, но MyInterface на самом деле не сравним, поэтому я предпочел бы не делать этого, если не нужно.Если возможно, я хотел бы знать, почему / где opCmp вызывается для анонимных классов, и как я могу предотвратить или обойти его.

Примечание. Реализация opCmp по умолчанию в Object включает в себя комментарий неопределенноссылка на ошибку, закомментированное сравнение адресов памяти, а затем выбрасывающую версию.

Спасибо!

Редактировать: я должен упомянуть, я пробовал и windbg, и ddbg, чтобы отследить, где именно находится opCmpвызывается, но не удалось в обоих случаях.Windbg не дал никакой полезной информации, потому что он упорно отказывался загружать любые символы, ddbg загружены символы, но исключение происходит во время инициализации (после статических конструкторов модуля, но перед основным) и предположительно ddbg не имел доступ к druntime символам?

1 Ответ

0 голосов
/ 19 марта 2012

Обновление: у меня проблемы с воспроизведением ошибки opCmp, особенно в примерах игрушек, но я думаю, что выяснил, что происходит.
Кажется, что создание анонимных внутренних классов, которые наследуют интерфейсы внутри анонимных функций, является ошибочным (см. Рисунок). В частности, анонимные классы и не очень хорошо себя ведут по отношению к виртуальным функциям. Даже с определенным opCmp у меня были ошибки с toString и конструкторами по умолчанию, и были члены, которые просто ничего не делают (но не выдают или выдают ошибку при вызове). __traits(allMembers, MyInterface) возвращает ожидаемую информацию, как и __traits(allMembers, typeof(anonInstance)), но вызов часто встречающихся участников не работает. Weird.
Но если я изменю интерфейс на класс с абстрактными методами, ошибка opCmp разрешается, анонимный класс ведет себя как ожидалось и т. Д. Я не знаю много о компиляторах, но я думаю, что во время компиляции создается таблица символов, которая сопоставляет имена виртуальных функций с адресами памяти, хранящимися в vtbl. Я думаю, что происходит то, что генерируемая карта меняется при возврате анонимного класса, полученного из интерфейса. Это возможно, потому что интерфейсы поддерживают множественное наследование и поэтому не могут предписывать абсолютное отображение vtbl. Классы, однако, могут потребовать, чтобы все наследники придерживались одной и той же схемы отображения (я не знаю, делают ли они, но могли бы), и поэтому анонимные классы не могут получить другое отображение.
Опять же, я действительно не уверен, но кажется, что это соответствует симптому, вызывается opCmp, хотя я нигде не использовал его. Я не думаю, что именно opCmp был проблемой, я думаю, что все виртуальные функции, определенные в Object, уязвимы. Я смог поддержать это следующим:

testopcmphelper.d
interface TestInterface {
    string helloWorld();
}
class TestClass {
    abstract string helloWorld();
}

testopcmp.d
import testopcmphelper;
import std.stdio;

void invokeFn(TestInterface function() f) {
    auto t = f();
    auto s = t.helloWorld();
    writeln(s);
}

unittest {
    auto f = function() {
        return new class TestInterface {
            string helloWorld() {
                return "Hello World!";
            }
        };
    };
    invokeFn(f);
}

void invokeFn(TestClass function() f) {
    auto t = f();
    auto s = t.helloWorld();
    writeln(s);
}

unittest {
    auto f = function() {
        return new class TestClass {
            string helloWorld() {
                return "Goodbye World!";
            }
        };
    };
    invokeFn(f);
}

Какие отпечатки:

src.utilities.testopcmp.__unittest2.__funcliteral1.__anonclass10
Goodbye World!

Указывает, что invokeFn(TestInterface) вызывает Object.toString вместо TestInterface.helloWorld.

Я собираюсь оставить вопрос открытым на другой день, если я допустил ошибку. Я, вероятно, тогда сообщу об этом как об ошибке в DMD. Я буду обходить эту проблему, используя только абстрактные классы для базовых типов анонимных функций фабрики. TL; DR Кажется, что это ошибка.

...