Вот то, что я опубликовал в другом месте относительно этого:
Итак, здесь есть пара вещей, на большинство из которых ссылаются @ltierney и / или @kalibera, но которые, возможно, могут извлечь выгоду изболее конкретные возможные шаблоны кодирования.
Суть вопроса заключается в том, чтобы оперативно изменить полезную нагрузку SEXP путем записи в элементы его памяти с адресом DATAPTR.Это когда-либо нормально делать (да), это может быть безопасно делать без подтверждения / гарантии, что это нормально делать (нет, никогда).А для объектов, созданных в одном и том же фрагменте кода C / C ++, у вас могут быть такие априорные гарантии, но вы не собираетесь использовать объекты, переданные из R.
Если говорить конкретно, я незнаю, что в любое время было бы нормально записать указатель, возвращаемый INTEGER()
на SEXP, который живет в R-пространстве, без предварительной проверки MAYBE_SHARED()
.Если MAYBE_SHARED(x)
возвращает FALSE, тогда все в порядке, и вы можете приступить к записи в указатель, точно так же, как это сделал ваш код.
Если MAYBE_SHARED(x) == TRUE
, вам нужно выполнить дублирование, выполнить операцию с копией, а затемверни это.Когда вы находитесь в коде C / C ++, на уровне указателей на данные, то вы, ваш код, должны явно вызывать это дублирование, защищать новый дублированный результат и т. Д.
Теперь причина в том, чтоэта конкретная вещь происходит в случае с компактной последовательностью, которая заключается в том, что, если сам R не построен определенным не по умолчанию способом, компактные последовательности ВСЕГДА имеют NAMED(x) == MAXNAMED
(то есть 2) с точки создания.Как указал Люк, это может измениться, но в настоящее время это дизайн.Таким образом, даже в ситуации .Call, когда замыкание не заставляет подсчитывать NAMED
, компактные последовательности всегда будут нуждаться в дублировании перед встроенной модификацией.И хотя это выбор, который мы могли бы сделать другим в случае компактной последовательности, точка Люка о других ALTREP более важна.
Могут быть ALTREP SEXP, где память, на которую указывает указатель, возвращается, например,INTEGER
буквально не записываемая память по той или иной причине.Способ, которым эти классы ALTREP объявят, - это то же самое, что делают компактные последовательности, помечая себя как «IMMUTABLE», то есть, делая MARK_NOT_MUTABLE(x)
(который в настоящее время устанавливает NAMED
в MAXNAMED
, но в будущем-защищен от возможного изменения подсчета ссылок).Это объявляет контракт о том, что SEXP должен дублироваться перед любым кодом, который захватывает указатель данных и записывает в него.
В конечном счете, я согласен, что это действительно странное неожиданное поведение, но из-за неспособности выполнить контракткоторый всегда был там.Возможно, в некоторых случаях в прошлом было безопасно (и я все еще сомневаюсь в этом) игнорировать / проявлять слабость, но с появлением ALTREP теперь всегда нужно следовать по причинам, изложенным в этой теме.
Таким образом, весь весь код, который собирается получить dataptr из ранее существующего (R-уровня) SEXP и записать в него, должен следовать шаблону вдоль (или эквивалентно осторожному):
SEXP awesomefun(SEXP x)
{
int nprot = 0;
if(MAYBE_SHARED(x)) {
PROTECT(x = duplicate(x)); nprot++;
}
/* do awesome things to x that modify it inline
protect other things as necessary but always increment nprot when you do,
decrement nprot if you ever unprotect */
if(nprot) UNPROTECT(nprot);
return x;
}
Любой код, который записывает данные в указатели данных, извлеченные из SEXP, которые он сам не создал (т. Е. Все, что происходит со стороны R) без выполнения этого, уже нарушал контракт C-API, но теперь также является небезопасным ALTREP, так какпоказывал мотивирующий пример.
И, еще раз, помните, компактные последовательности могли вести себя по-разному, так что этот код работал, но другие классы ALTREP (Люк упоминает ALTREPS, поддерживаемые отображенным в файл файлом) не могли,поэтому поведение компактных последовательностей на самом деле не является проблемой.
Я надеюсь, что это полезно и проясняет ситуациюer.
Best