Сокращение проблемы до MRE :
class foo {}
say(foo.new); # Cannot stringify ...
Упрощение решения:
class foo { method Str () { 'foo' } }
say(foo.new); # foo
В итоге добавьте метод Str
.
Это звучит просто, но есть много закулисных вещей, которые нужно рассмотреть / объяснить.
nqp vs raku
Приведенное выше решение - та же самая техника, которую использует raku; когда подпрограмма / операция предполагает, что значение является строкой, но это не так, поведение языка заключается в попытке привести к строке. В частности, посмотрите, есть ли метод Str
, который может быть вызван для значения, и, если это так, вызовите его.
В этом случае NQP NQPMu
, что намного больше скелетов чем raku's Mu
, не предусмотрен метод Str
по умолчанию. Поэтому решение состоит в том, чтобы добавить его вручную.
В целом, NQP является довольно враждебным языком, если вы не достаточно хорошо знаете raku и прошли курс по внутренним компонентам Rakudo и NQP .
И как только вы освоите материал в этом курсе, я рекомендую вам рассмотреть IR C каналы # raku-dev и / или # moarvm как ваш первый порт вызова, а не SO (если только ваша цель не состоит в том, чтобы конкретно увеличить охват SO nqp / moarvm).
Отладка кода компилятора
Как вы уже видели, код NQP вы связали вызовы .say
в файловом дескрипторе.
Затем вызывается этот метод .
Тело этого метода - $str ~ "\n"
. Этот код будет пытаться привести $str
к строке (так же, как в raku). Это то, что будет генерировать ошибку «Cannot stringify».
Поиск «Cannot stringify» в репозитории NQP соответствует только некоторому коду Java. Бьюсь об заклад, вы не запускаете Rakudo на JVM. Это означает, что сообщение об ошибке должно приходить от MoarVM.
Тот же поиск в репозитории MoarVM дает эту строку в coerce.c
в MoarVM .
Взгляд назад в подпрограмма, содержащая эту строку, мы видим этот бит :
/* Check if there is a Str method. */
MVMROOT(tc, obj, {
strmeth = MVM_6model_find_method_cache_only(tc, obj,
tc->instance->str_consts.Str);
});
Это показывает, что бэкэнд, написанный в C, ищет и вызывает «метод» с именем Str
. (Он опирается на внутренний API (6model), которого придерживаются все три уровня компилятора (raku, nqp и backends).)
Настройка метода Str
Вы будете необходимо настроить метод Str
в зависимости от ситуации. Например, для печати имени класса, если это объект типа, и значения его атрибута $!bar
в противном случае:
class foo {
has $!bar;
method Str () { self ?? nqp::coerce_is($!bar) !! self.HOW.name(self) }
}
say(foo.new(bar=>42)); # 42
Несмотря на имя метода, подпрограмма nqp say
имеет значение not ожидал raku Str
, а скорее нативную строку nqp (которая в конечном итоге является нативной строкой MoarVM в бэкэнде MoarVM). Следовательно, потребность в nqp::coerce_is
(которую я нашел, просмотрев , что делает nqp ops c).
self.HOW.name(self)
- это еще один пример того, как nqp просто не имеет тонкости, которые есть у раку. Вы могли бы написать тот же код в raku, но идиоматический c способ написать его в raku - self.^name
.