2016-06-18 6 views
4

Я в настоящее время пытаюсь расширить sqrat (утилита привязки белки), чтобы удовлетворить привязку lambdas к белке.Хранение C++ lambdas как двоичные данные

Проблема у меня в том, что хотя хранение и ссылаясь на код может знать о подписи лямбда (в Вот код, чтобы создать функцию, которая будет выполнять лямбда)

// Arg Count 0 
template <class R, class Args, class Arity> 
SQFUNCTION SqMemberLambdaFuncDetail(R result, 
            Args args, 
            boost::mpl::integral_c<unsigned int, 1> arity) 
{ 
    return &SqLambda<R>::template Func0<false>; 
} 

// Arg Count 1 
template <class R, class Args, class Arity> 
SQFUNCTION SqMemberLambdaFuncDetail(R result, 
            Args args, 
            boost::mpl::integral_c<unsigned int, 2> arity) 
{ 
    return &SqLambda<R>::template Func1<boost::mpl::at_c<Args, 1>::type, 2, false>; 
} 

template <class F> 
SQFUNCTION SqMemberLambdaFunc(F f) 
{ 
    typedef boost::function_types::result_type<decltype(&F::operator())>::type result_t; 
    typedef boost::function_types::function_arity< decltype(&F::operator())> arity_t; 
    typedef boost::function_types::parameter_types< decltype(&F::operator())>::type args_t; 
    result_t result; 
    args_t args; 
    arity_t arity; 
    return SqMemberLambdaFuncDetail<result_t, args_t, arity_t>(result, args, arity); 
} 

сам лямбда необходимо хранить в анонимной форме, как двоичные данные, и я не могу найти способ сделать это.

+0

Я не понял все это, но по существу вы хотите сериализовать захваченные значения? Или целая функция? Что насчет ссылок и т. Д.? – deviantfan

+1

Нужно ли десериализовать сериализованную версию лямбда после выхода текущего экземпляра текущей программы? –

+0

Могли бы вы рассказать о том, как на самом деле вы будете использовать это? Я не понимаю, почему вам нужно сериализовать lambdas, когда все, что вам нужно, это создать привязку к языку ...? –

ответ

3

Lambdas не гарантируется как стандартная компоновка (поэтому просмотр их битов никогда не является законным), и их содержание не является интроспективным.

Если вам нужен код, который можно сериализовать, а затем запустить удаленно, используйте механизм сценариев. Их много, а некоторые хорошо взаимодействуют с C++.

Или просто увеличивайте феникс, что делает отражающим код C++ - esque, который является лямбда-эск.

Однако, если ваша единственная проблема хранения копию из лямбда с подписью R(Args...) в общем, просто использовать std::function<R(Args...)>.

+0

Я использую механизм сценариев, вот что такое белка, однако мне нужно связать с ним код C++ (чтобы он мог скриптировать все, что нужно для сценариев). Я могу связывать глобальные функции и методы классов уже, но возможность привязки лямбда была бы очень полезна, следовательно, мои попытки сделать это. –

+1

@JonHodgson Вы можете связывать глобальные функции и методы класса, не сохраняя их как двоичные данные. Почему, по вашему мнению, это необходимо делать при привязке лямбда? – Oktalist

+0

@ Окталист Я думаю, что мое мышление сменилось неправильным путем, я был обеспокоен тем, что лямбда выходит из сферы действия после того момента, когда я назвал метод привязки, и, таким образом, был уничтожен до того, как он будет использоваться. Теперь я вижу, что мне придется иметь дело с этой проблемой по-другому. –

2

К сожалению, C++ по дизайну создает непроницаемую стену между кодом и данными.

Лямбда-код (плюс захваченные данные) и не может рассматриваться как данные (например, хранится на диске, отправляется по сети, проверяется). Что C++ позволяет вам рассматривать как данные указатели на код, но не сам код, который построен только для компиляции и неизменен. Более того, указатели действительны только в одной и той же программе (отправка указателя на какую-либо другую программу бессмысленна, поскольку она имеет смысл только в определенном адресном пространстве).

Выходной только - это реализовать (или включить) полный язык программирования, который позволяет создавать пользовательский код во время выполнения.

+0

Включение полного языка программирования (белка) - вот для чего мне нужна эта функциональность! .. но спасибо за то, что я уточнил некоторые вещи. Это то, что я на самом деле знал, но как-то потерял из виду, когда меня похоронили в метапрограммировании шаблонов, так что теперь вы вернули его в фокус, я вижу, что мне нужно немного переосмыслить. –

2

Вы можете создать класс LambdaWrapper, который будет хранить сам лямбда (например, в boost::function или std::function). Это будет обычный класс, поэтому вы сможете передать его белке, как обычный класс.

+0

Это, похоже, было решением. Я создал класс LambdaWrapperBase (чтобы я мог хранить указатели на множество LambdaWrappers в контейнере), а затем шаблонный класс LambdaWrapper, который унаследовал от него. Кажется, что это работает для некоторых лямбдов, но у меня есть проблема, если моя лямбда ссылается на «это», что потенциально может сделать ее более полезной, хотя, как я могу ее использовать, для многих вещей, которые я думаю. –

+0

Нативные объекты C++, на которые ссылаются языки сценариев, должны проживать до тех пор, пока их копии в сценарии живут. Это проблема? Если нет, напишите, что произойдет. –

0

Что вы пытаетесь сделать, это выделить некоторую память на своем языке сценариев, сохранить внутри нее объект C++ и затем получить к нему доступ позже.

Общие проблемы вы сталкиваетесь являются:

  1. Сохранение объекта в соответствии с правилами C++.

  2. Возвращение.

Учитывая некоторый тип T, если этот тип копируемый или подвижен, вы всегда можете копировать/перемещать построить его в выделение, которое является достаточно большим, чтобы хранить T и правильно выровнено.Код, который довольно прост:

void func(T t) 
{ 
    void *alloc = GetAllocation(sizeof(T), alignof(T)); 
    new(alloc) T(t); 
} 

Где GetAllocation это то, что вам нужно сделать, чтобы выделить память в системе скриптов. Синтаксис размещения new будет строить объект типа T, в этом случае путем копирования с t. Вы также можете двигаться: new(alloc) T(std::move(t));.

О, и вам также понадобится выполнить любую гимнастику, которую требует ваша система сценариев, чтобы этот объект получил свой деструктор, как только скрипт закончит с ним. Обычно это связано с регистрацией какой-либо функции со скриптовой системой для этого конкретного объекта. Но разрушение объекта опускается на шаг 2.

Но это была легкая часть. Жесткая часть получает эти данные и снова использует их. Например, если скрипт пытается вызвать эту функцию, вам придется прикрепить к ней какой-то крючок, который позволит вашему коду знать, чтобы вызвать его. И когда это произойдет, оно должно восстановить построенное значение типа T.

Проблема для вас заключается в следующем: вы действительно не знаете, какой тип T есть.

Если функция была передана лямбдой, вы не знаете, что это такое. . Ох, вы можете использовать decltype для доступа к типу. Но это только потому, что для регистрации была передана значение этого типа.

На принимающей стороне, со своего языка сценариев, все, что у вас есть, это void*. И C++ не имеет способа записать, какой тип вы использовали напрямую.

Конечно, у C++ есть способы скрыть тип, чтобы впоследствии его можно было восстановить. Обычно это называется стиранием типа: у вас есть один тип, который может содержать значение многочисленных типов, если у них есть определенный интерфейс. И это позволит вам взаимодействовать со значением, не зная его типа.

std::function - такой тип объектов функции. Он может принимать любой тип, который является вызываемым: указатели на функции, указатели элементов, функторы и лямбды (aka: functors). Поскольку вызов - это то, что вы хотите, это должен быть приемлемый объект с стиранием.

До тех пор, пока вы знаете, какая подпись будет регистрироваться, каждая регистрационная функция будет регистрироваться. В конце концов, подпись для функций, которые она несет, является частью типа для std::function. Таким образом, вам придется стандартизировать такие функторы для конкретной сигнатуры, которая известна в то время, когда функтор зарегистрирован в системе сценариев.

У вас могут быть разные функции, которые регистрируют разные подписи. Действительно, вы можете создавать такие шаблоны функций. Но вам все равно нужно знать, когда определенная функция регистрируется, какова ее подпись.

Это также важно для уничтожения объекта во время GC, так как вам нужно его вернуть.

+0

Спасибо за полноту ответа. Это то, что я изначально пытался сделать, но я не мог заставить его работать, возможно, потому, что я ошибался в отношении объявлений std :: function (все это должно быть шаблоном, позволяющим автоматически обрабатывать различные подписи во время компиляции). Поэтому я пошел по маршруту, указанному в моем другом посте (который я теперь отредактировал, кстати), но, как вы знаете, столкнулся с другими проблемами. –

Смежные вопросы