% \iffalse meta-comment % % lt3rawobjects Objects and proxies in LaTeX3 % Copyright (C) 2022 Paolo De Donato % % This file is part of lt3rawobjects. % % lt3rawobjects is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % % lt3rawobjects is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with lt3rawobjects. If not, see . % % \fi % % \iffalse %<*driver> \ProvidesFile{lt3rawobjects.dtx}[2023/03/17 2.3 Objects and proxies in LaTeX3] % %\NeedsTeXFormat{LaTeX2e} %\RequirePackage{expl3}[2022-04-10] %\ProvidesExplPackage{lt3rawobjects}{2023/03/17}{ 2.3 }{Objects and proxies in LaTeX3} %<*driver> \documentclass[full]{l3doc} \usepackage{lt3rawobjects} \usepackage{xparse} \usepackage{xcolor} \usepackage{fancyvrb} \begin{document} \DocInput{lt3rawobjects.dtx} \end{document} % % \fi % % \NewDocumentCommand{\thpkg}{}{\pkg{lt3rawobjects}} % \NewDocumentCommand{\thvsn}{}{2.3} % \NewDocumentCommand{\thdta}{}{2023/03/17} % \NewDocumentCommand{\fromV}{ m }{{\ttfamily From: #1}} % \NewDocumentCommand{\fromVD}{ m m }{{\ttfamily From: #1}\par {\ttfamily Deprecated in: #2}} % % \NewDocumentCommand{\simfun}{m m}{\textcolor{blue}{\textbackslash #1}\textcolor{purple}{:#2}} % \NewDocumentCommand{\simvar}{m}{\textcolor{violet}{\textbackslash #1}} % \DefineVerbatimEnvironment{Coding}{Verbatim}{numbers=left, frame=single, commandchars={![]}} % % \title{The \pkg{lt3rawobjects} package} % \author{Paolo De Donato} % \date{Released on \thdta\ Version \thvsn} % % \maketitle % % \tableofcontents % % \begin{documentation} % % \section{Introduction} % Package \thpkg\ introduces a new mechanism to create and manage structured data called ``objects'' like the well known C structures. The functions exported by this package are quite low level, and many important mechanisms like member protection and name resolution aren't already defined and should be introduced by intermediate packages. Higher level libraries built on top of \thpkg\ could also implement an improved and simplified syntax since the main focus of \thpkg\ is versatility and expandability rather than common usage. % % This packages follows the \href{https://semver.org/}{SemVer} specification (\texttt{https://semver.org/}). In particular any major version update (for example from \texttt{1.2} to \texttt{2.0}) may introduce imcompatible changes and so it's not advisable to work with different packages that require different major versions of \thpkg. Instead changes introduced in minor and patch version updates are always backward compatible, and any withdrawn function is declared deprecated instead of being removed. % % \section{Addresses} % In this package a \emph{pure address} is any string without spaces (so a sequence of tokens with category code 12 ``other'') that uniquely identifies a resource or an entity. An example of pure address if the name of a control sequence \tn{\meta{name}} that can obtained by full expanding \cs{cs_to_str:N} \tn{\meta{name}}. Instead an \emph{expanded address} is a token list that contains only tokens with category code 11 (letters) or 12 (other) that can be directly converted to a pure address with a simple call to \cs{tl_to_str:n} or by assigning it to a string variable. % % An \emph{address} is instead a fully expandable token list which full expansion is an expanded address, where full expansion means the expansion process performed inside |c|, |x| and |e| parameters. Moreover, any address should be fully expandable according to the rules of |x| and |e| parameter types with same results, and the name of control sequence resulting from a |c|-type expansion of such address must be equal to its full expansion. For these reasons addresses should not contain parameter tokens like |#| (because they're threat differently by |x| and |e|) or control sequences that prevents expansion like \cs{exp_not:n} (because they leave unexpanded control sequences after an |x| or |e| expansion, and expanded addresses can't have control sequences inside them). In particular, |\tl_to_str:n{ ## }| is \emph{not} a valid address (assuming standard category codes). % % Addresses could be not full expanded inside an |f| argument, thus an address expanded in an |f| argument should be |x|, |e| or |c| expended later to get the actual pure address. If you need to fully expand an address in an |f| argument (because, for example, your macro should be fully expandable and your engine is too old to support |e| expansion efficiently) then you can put your address inside \cs{rwobj_address_f:n} and pass them to your function. For example, % \begin{verbatim} % \your_function:f{ \rwobj_address_f:n { your \address } } % \end{verbatim} % Remember that \cs{rwobj_address_f:n} only works with addresses, can't be used to fully expand any token list. % % Like functions and variables names, pure addresses should follows some basic naming conventions in order to avoid clashes between addresses in different modules. Each pure address starts with the \meta{module} name in which such address is allocated, then an underscore (|_|) and the \meta{identifier} that uniquely identifies the resource inside the module. The \meta{module} should contain only lowercase ASCII letters. % % A \emph{pointer} is just a \LaTeX3 string variable that holds a pure address. We don't enforce to use |str| or any special suffix to denote pointers so you're free to use |str| or a custom \meta{type} as suffix for your pointers in order to distinguish between them according to their type. % % In \thpkg\ all the macros ending with |_adr| or |_address| are fully expandable and can be used to compose valid addresses as explained in their documentation. % % \section{Objects} % An \emph{object} is just a collection of several related entities called \emph{fields}. Objects are themselves entities so they have addresses and could be contained inside other objects. Objects addresses are also used to compose the addresses of each of their inner entity, thus different objects can have fields with the same name without clashing each other. Each object is uniquely identified by its pure address, which is composed by a \meta{module} and an \meta{identifier} as explained before. The use of underscore character in objects identifiers is reserved. You can retrive the address of an object via the \cs{object_address:nn} function. % % Objects are always created from already existing objects. An object that can be used to create other objects is called |proxy|, and the proxy that has created an object is its \emph{generator}. In the |rawobjects| module is already allocated a particular proxy that can be used to create every other proxy. Its identifier is just |proxy| and its pure address is stored in \cs{c_proxy_address_str}. The functions \cs{object_create} can be used to create new objects. % % \section{Fields} % Remember that objects are just a collection of different fields uniquely identidied by a pure address. Here an field could be one of the following entities: % \begin{itemize} % \item a \LaTeX3 variable, in which case the field is called \emph{member}; % \item a \LaTeX3 constant, in which case the field is called just \emph{constant}; % \item a \LaTeX3 function, in which case the field is called \emph{method}; % \item generic control sequences, in which case the field is called simply \emph{macro}; % \item an entire object, in which case the field is called \emph{embedded object}. % \end{itemize} % % Objects could be declared \emph{local} or \emph{global}. The only difference between a local and a global object is the scope of their members (that are \LaTeX3 variables). You should always create global object unless you specifically need local members. % % \subsection{Constants} % Constants in an object could be \emph{near} and \emph{remote}. A near constant is just a constant declared in such object and could be referred only by it, instead a remote constant is declared inside its generator and can be referred by any object created from that proxy, thus it's shared between all the generated objects. Functions in this library that work with near constants usually contain |ncmember| in their names, whereas those involving remore constants contain |rcmember| instead. % % Both near and remote constants are created in the same way via the |_newconst| functions, however remote constant should be created in a proxy whereas near contant are created directly in the target object. % % \subsection{Methods} % Methods are \LaTeX3 functions that can't be changed once they're created. Like constant, methods could be near or remote. Moreover, functions in this library dealing with near methods contain |ncmethod| whereas those dealing with remote methods contain |rcmethod| in their names. % % \subsection{Members} % Members are just mutable \LaTeX3 variables. You can manually create new members in already existing objects or you can put the definition of a new member directly in a proxy with the \cs{proxy_push_member} functions. In this way all the objects created with that proxy will have a member according to such definition. If the object is local/global then all its members are automatically local/global. % % A member can be \emph{tracked} or \emph{not tracked}. A tracked member have additional information, like its type, stored in the object or in its generator. In particular, you don't need to specify the type of a tracked member and some functions in \thpkg\ are able to retrieve the required information. All the members declared in the generator are automatically tracked. % % \section{Object members} % Sometimes it's necessary to store an instance of an object inside another object, since objects are structured entities that can't be entirely contained in a single \LaTeX3 variable you can't just put it inside a member or constant. However, there are some very easy workarounds to insert object instances as fields of other objects. % % For example, we're in module |MOD| and we have an object with id |PAR|. We want to provide |PAR| with a field that holds an instance of an object created by proxy |PRX|. We can achieve this in three ways: % % \subsection{Create a pointer member} % We first create a new object from |PRX| % \begin{Coding} % !simfun[object_create][nnn] % { !simfun[object_address][nn] { MOD }{ PRX } }{ MOD }{ INST } % \end{Coding} % then we create an |str| member in |PAR| that will hold the address of the newly created object. % \begin{Coding} % !simfun[object_new_member][nnn] % { % !simfun[object_address][nn] { MOD }{ PAR } % }{ pointer }{ str } % % !simfun[object_member_set][nnnx] % { % !simfun[object_address][nn] { MOD }{ PAR } % } % { pointer }{ str } % { % !simfun[object_address][nn] { MOD }{ INST } % } % \end{Coding} % % You can then get the pointed object by just using the |pointer| member. Notice that you're not force to use the |str| type for the pointer member, but you can also use |tl| or any custom \meta{type}. In the latter case be sure to at least define the following functions: \cs{\meta{type}_new:c}, \cs{\meta{type}_(g)set:cn} and \cs{\meta{type}_use:c}. % % \subsubsection*{Advantages} % \begin{itemize} % \item Simple and no additional function needed to create and manage included objects; % \item you can share the same object between different containers; % \item included objects are objects too, you can use address stored in pointer member just like any object address. % \end{itemize} % % \subsubsection*{Disadvantages} % \begin{itemize} % \item You must manually create both the objects and link them; % \item if you forgot to properly initialize the pointer member it'll contain the ``null address'' (the empty string). Despite other programming languages the null address is not treated specially by \thpkg, which makes finding null pointer errors more difficult. % \end{itemize} % % \subsection{Clone the inner structure} % Anoter solution is to copy the members declared in |PRX| to |PAR|. For example, if in |PRX| are declared a member with name |x| and type |str|, and a member with name |y| and type |int| then % \begin{Coding} % !simfun[object_new_member][nnn] % { % !simfun[object_address][nn] { MOD }{ PAR } % }{ prx-x }{ str } % !simfun[object_new_member][nnn] % { % !simfun[object_address][nn] { MOD }{ PAR } % }{ prx-y }{ int } % \end{Coding} % % \subsubsection*{Advantages} % \begin{itemize} % \item Very simple; % \item no hidden item is created, this procedure has the lowest overhead among all the proposed solutions here. % \end{itemize} % % \subsubsection*{Disadvantages} % \begin{itemize} % \item If you need the original instance of the stored object then you should create a temporary object and manually copy each field to it. Don't use this method if you later need to retrieve the stored object entirely and not only its fields. % \end{itemize} % % \subsection{Embedded objects} % From \thpkg\ \texttt{2.2} you can put \emph{embedded objects} inside objects. Embedded objects are created with \cs{embedded_create} function % \begin{Coding} % !simfun[embedded_create][nnn] % { % !simfun[object_address][nn] { MOD }{ PAR } % } % { PRX }{ emb } % \end{Coding} % and addresses of emmbedded objects can be retrieved with function \cs{object_embedded_adr}. You can also put the definition of embedded objects in a proxy by using \cs{proxy_push_embedded} just like \cs{proxy_push_member}. % % \subsubsection*{Advantages} % \begin{itemize} % \item You can put a declaration inside a proxy so that embedded objects are automatically created during creation of parent object; % \item included objects are objects too, you can use address stored in pointer member just like any object address. % \end{itemize} % % \subsubsection*{Disadvantages} % \begin{itemize} % \item Needs additional functions available for version \texttt{2.2} or later; % \item embedded objects must have the same scope and visibility of parent one; % \item creating objects also creates additional hidden variables, taking so (little) additional space. % \end{itemize} % % \section{Library functions} % \label{sec:lib} % % \subsection{Common functions} % % \begin{function}[EXP]{\rwobj_address_f:n} % \begin{syntax} % \cs{rwobj_address_f:n} \Arg{address} % \end{syntax} % Fully expand an address in an |f|-type argument. % % \fromV{2.3} % \end{function} % % \subsection{Base object functions} % % \begin{function}[rEXP]{\object_address:nn} % \begin{syntax} % \cs{object_address:nn} \Arg{module} \Arg{id} % \end{syntax} % Composes the address of object in module \meta{module} with identifier \meta{id} and places it in the input stream. Notice that both \meta{module} and \meta{id} are converted to strings before composing them in the address, so they shouldn't contain any command inside. % % \fromV{1.0} % \end{function} % % \begin{function}{\object_address_set:Nnn, \object_address_gset:Nnn} % \begin{syntax} % \cs{object_address_set:nn} \meta{str var} \Arg{module} \Arg{id} % \end{syntax} % Stores the adress of selected object inside the string variable \meta{str var}. % % \fromV{1.1} % \end{function} % % \begin{function}[rEXP]{\object_embedded_adr:nn, \object_embedded_adr:Vn} % \begin{syntax} % \cs{object_embedded_adr:nn} \Arg{address} \Arg{id} % \end{syntax} % Compose the address of embedded object with name \meta{id} inside the parent object with address \meta{address}. Since an embedded object is also an object you can use this function for any function that accepts object addresses as an argument. % % \fromV{2.2} % \end{function} % % \begin{function}[pTF]{\object_if_exist:n, \object_if_exist:V} % \begin{syntax} % \cs{object_if_exist_p:n} \marg{address} % \cs{object_if_exist:nTF} \marg{address} \marg{true code} \marg{false code} % \end{syntax} % Tests if an object was instantiated at the specified address. % % \fromV{1.0} % \end{function} % % \begin{function}[EXP]{\object_get_module:n, \object_get_module:V, \object_get_proxy_adr:n, \object_get_proxy_adr:V} % \begin{syntax} % \cs{object_get_module:n} \marg{address} % \cs{object_get_proxy_adr:n} \marg{address} % \end{syntax} % Get the object module and its generator. % % \fromV{1.0} % \end{function} % % \begin{function}[pTF]{\object_if_local:n, \object_if_global:n, \object_if_local:V, \object_if_global:V} % \begin{syntax} % \cs{object_if_local_p:n} \marg{address} % \cs{object_if_local:nTF} \marg{address} \marg{true code} \marg{false code} % \end{syntax} % Tests if the object is local or global. % % \fromV{1.0} % \end{function} % % \begin{function}[pTF]{\object_if_public:n, \object_if_private:n, \object_if_public:V, \object_if_private:V} % \begin{syntax} % \cs{object_if_public_p:n} \marg{address} % \cs{object_if_public:nTF} \marg{address} \marg{true code} \marg{false code} % \end{syntax} % Tests if the object is public or private. % % \fromV{1.0} % \end{function} % % \subsection{Members} % % \begin{function}[rEXP]{\object_member_adr:nnn, \object_member_adr:Vnn, \object_member_adr:nnv, \object_member_adr:nn, \object_member_adr:Vn} % \begin{syntax} % \cs{object_member_adr:nnn} \marg{address} \marg{member name} \marg{member type} % \cs{object_member_adr:nn} \marg{address} \marg{member name} % \end{syntax} % Fully expands to the address of specified member variable. If the member is tracked then you can omit the type field. % % \fromV{1.0} % \end{function} % % \begin{function}[pTF]{\object_member_if_exist:nnn, \object_member_if_exist:Vnn} % \begin{syntax} % \cs{object_member_if_exist_p:nnn} \marg{address} \marg{member name} \marg{member type} % \cs{object_member_if_exist:nnnTF} \marg{address} \marg{member name} \marg{member type} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the specified member exist. % % \fromV{2.0} % \end{function} % % \begin{function}[pTF]{\object_member_if_tracked:nn, \object_member_if_tracked:Vn} % \begin{syntax} % \cs{object_member_if_tracked_p:nn} \Arg{address} \Arg{member name} % \cs{object_member_if_tracked:nnTF} \Arg{address} \Arg{member name} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the specified member exist and is tracked. % % \fromV{2.3} % \end{function} % % \begin{function}[EXP]{\object_member_type:nn, \object_member_type:Vn} % \begin{syntax} % \cs{object_member_type:nn} \marg{address} \marg{member name} % \end{syntax} % Fully expands to the type of specified tracked member. % % \fromV{1.0} % \end{function} % % \begin{function}{\object_new_member:nnn, \object_new_member:Vnn, \object_new_member:nnv} % \begin{syntax} % \cs{object_new_member:nnn} \marg{address} \marg{member name} \marg{member type} % \end{syntax} % Creates a new member with specified name and type. The created member is not tracked. % % \fromV{1.0} % \end{function} % % \begin{function}{\object_new_member_tracked:nnn, \object_new_member_tracked:Vnn} % \begin{syntax} % \cs{object_new_member_tracked:nnn} \marg{address} \marg{member name} \marg{member type} % \end{syntax} % Creates a new tracked member. % % \fromV{2.3} % \end{function} % % \begin{function}[EXP]{\object_member_use:nnn, \object_member_use:Vnn, \object_member_use:nnv, \object_member_use:nn, \object_member_use:Vn} % \begin{syntax} % \cs{object_member_use:nnn} \marg{address} \marg{member name} \marg{member type} % \cs{object_member_use:nn} \marg{address} \marg{member name} % \end{syntax} % Uses the specified member variable. % % \fromV{1.0} % \end{function} % % \begin{function}{\object_member_set:nnnn, \object_member_set:nnvn, \object_member_set:Vnnn, \object_member_set:nnn, \object_member_set:Vnn} % \begin{syntax} % \cs{object_member_set:nnnn} \Arg{address} \Arg{member name} \Arg{member type} \Arg{value} % \cs{object_member_set:nnn} \Arg{address} \Arg{member name} \Arg{value} % \end{syntax} % Sets the value of specified member to \marg{value}. It calls implicitly \cs{\meta{member type}_(g)set:cn} then be sure to define it before calling this method. % % \fromV{2.1} % \end{function} % % \begin{function}{\object_member_set_eq:nnnN, \object_member_set_eq:nnvN, \object_member_set_eq:VnnN, \object_member_set_eq:nnnc, \object_member_set_eq:Vnnc, \object_member_set_eq:nnN, \object_member_set_eq:VnN, \object_member_set_eq:nnc, \object_member_set_eq:Vnc} % \begin{syntax} % \cs{object_member_set_eq:nnnN} \marg{address} \marg{member name} \marg{member type} \meta{variable} % \cs{object_member_set_eq:nnN} \marg{address} \marg{member name} \meta{variable} % \end{syntax} % Sets the value of specified member equal to the value of \meta{variable}. % % \fromV{1.0} % \end{function} % % \begin{function}{\object_member_generate:NN, \object_member_generate_protected:NN} % \begin{syntax} % \cs{object_member_generate:NN} \cs{\meta{name\textsubscript{1}}} \cs{\meta{name\textsubscript{2}}:\meta{arg1}\meta{args}} % \end{syntax} % Define the new functions \cs{\meta{name\textsubscript{1}}:nnn\meta{Targs} } and \cs{\meta{name\textsubscript{1}}:nn\meta{Targs} } that pass to \cs{\meta{name\textsubscript{2}}:\meta{arg1}\meta{args} } the specified member address as the first argument. \meta{Targs} is a list of argument specifications obtained by transforming each element of \meta{args} to |n|, |N|, |w|, |T| or |F|. % % The first three parameters of \cs{\meta{name\textsubscript{1}}:nnn\meta{args} } should be in the following order: % \begin{enumerate} % \item an object address; % \item a member name; % \item the type of specified member. % \end{enumerate} % % Function \cs{\meta{name\textsubscript{1}}:nn\meta{args} } only accepts the first two parameters and works only with tracked members. Notice that \meta{arg1} must be only one of the following: |n|, |c|, |v|, |x|, |f|, |e|, |o|. % % \fromV{2.3} % \end{function} % % \begin{function}{\object_member_generate_inline:Nnn, \object_member_generate_protected_inline:Nnn} % \begin{syntax} % \cs{object_member_generate_inline:Nnn} \cs{\meta{name\textsubscript{1}}} \Arg{name\textsubscript{2}} \{\meta{arg1}\meta{args}\} % \end{syntax} % Works as \cs{object_member_generate:NN}, however in \meta{name\textsubscript{2}} you can use parameters |#1| and |#2| to compose the needed function. Parameter |#1| expands to the (fully expanded) member type and |#2| is equal to |g| if the object is global and it's empty if it is local. % % \fromV{2.3} % \end{function} % % \subsection{Constants} % \begin{function}[rEXP]{\object_ncmember_adr:nnn, \object_ncmember_adr:Vnn, \object_ncmember_adr:vnn, \object_rcmember_adr:nnn, \object_rcmember_adr:Vnn} % \begin{syntax} % \cs{object_ncmember_adr:nnn} \Arg{address} \Arg{member name} \Arg{member type} % \end{syntax} % Fully expands to the address of specified near/remote constant member. % % \fromV{2.0} % \end{function} % % \begin{function}[pTF]{\object_ncmember_if_exist:nnn, \object_ncmember_if_exist:Vnn, \object_rcmember_if_exist:nnn, \object_rcmember_if_exist:Vnn} % \begin{syntax} % \cs{object_ncmember_if_exist_p:nnn} \marg{address} \marg{member name} \marg{member type} % \cs{object_ncmember_if_exist:nnnTF} \marg{address} \marg{member name} \marg{member type} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the specified member constant exist. % % \fromV{2.0} % \end{function} % % \begin{function}[EXP]{\object_ncmember_use:nnn, \object_ncmember_use:Vnn, \object_rcmember_use:nnn, \object_rcmember_use:Vnn} % \begin{syntax} % \cs{object_ncmember_use:nnn} \Arg{address} \Arg{member name} \Arg{member type} % \end{syntax} % Uses the specified near/remote constant member. % % \fromV{2.0} % \end{function} % % \begin{function}{\object_ncmember_generate:NN, \object_ncmember_protected_generate:NN, \object_rcmember_generate:NN, \object_rcmember_protected_generate:NN} % \begin{syntax} % \cs{object_ncmember_generate:NN} \cs{\meta{name\textsubscript{1}}} \cs{name\textsubscript{2}}:\meta{arg1}\meta{args} % \end{syntax} % Works as \cs{object_member_generate:NN} but with constants instead of members. % % \fromV{2.3} % \end{function} % % \begin{function}{\object_ncmember_generate_inline:Nnn, \object_ncmember_protected_generate_inline:Nnn, \object_rcmember_generate_inline:Nnn, \object_rcmember_protected_generate_inline:Nnn} % \begin{syntax} % \cs{object_ncmember_generate_inline:Nnn} \cs{\meta{name\textsubscript{1}}} \Arg{name\textsubscript{2}} \{\meta{arg1}\meta{args}\} % \end{syntax} % Works as \cs{object_member_generate_inline:Nnn} but with constants instead of members. % % \fromV{2.3} % \end{function} % % \subsection{Methods} % \begin{function}[rEXP]{\object_ncmethod_adr:nnn, \object_ncmethod_adr:Vnn, \object_ncmethod_adr:vnn, \object_rcmethod_adr:nnn, \object_rcmethod_adr:Vnn} % \begin{syntax} % \cs{object_ncmethod_adr:nnn} \Arg{address} \Arg{method name} \Arg{method variant} % \end{syntax} % Fully expands to the address of the specified % \begin{itemize} % \item near constant method if \cs{object_ncmethod_adr} is used; % \item remote constant method if \cs{object_rcmethod_adr} is used. % \end{itemize} % % \fromV{2.0} % \end{function} % % \begin{function}[pTF]{\object_ncmethod_if_exist:nnn, \object_ncmethod_if_exist:Vnn, \object_rcmethod_if_exist:nnn, \object_rcmethod_if_exist:Vnn} % \begin{syntax} % \cs{object_ncmethod_if_exist_p:nnn} \marg{address} \marg{method name} \marg{method variant} % \cs{object_ncmethod_if_exist:nnnTF} \marg{address} \marg{method name} \marg{method variant} \Arg{true code} \Arg{false code} % \end{syntax} % Tests if the specified method constant exist. % % \fromV{2.0} % \end{function} % % \begin{function}{\object_new_cmethod:nnnn, \object_new_cmethod:Vnnn} % \begin{syntax} % \cs{object_new_cmethod:nnnn} \Arg{address} \Arg{method name} \Arg{method arguments} \Arg{code} % \end{syntax} % Creates a new method with specified name and argument types. The \marg{method arguments} should be a string composed only by |n| and |N| characters that are passed to \cs{cs_new:Nn}. % % \fromV{2.0} % \end{function} % % \begin{function}[EXP]{\object_ncmethod_call:nnn, \object_ncmethod_call:Vnn, \object_rcmethod_call:nnn, \object_rcmethod_call:Vnn} % \begin{syntax} % \cs{object_ncmethod_call:nnn} \marg{address} \marg{method name} \marg{method variant} % \end{syntax} % Calls the specified method. This function is expandable if and only if the specified method was not declared |protected|. % % \fromV{2.0} % \end{function} % % \subsection{Creation of constants} % \begin{function}{\object_newconst_tl:nnn, \object_newconst_tl:Vnn, \object_newconst_str:nnn, \object_newconst_str:Vnn, \object_newconst_int:nnn, \object_newconst_int:Vnn, \object_newconst_clist:nnn, \object_newconst_clist:Vnn, \object_newconst_dim:nnn, \object_newconst_dim:Vnn, \object_newconst_skip:nnn, \object_newconst_skip:Vnn, \object_newconst_fp:nnn, \object_newconst_fp:Vnn} % \begin{syntax} % \cs{object_newconst_\meta{type}:nnn} \Arg{address} \Arg{constant name} \Arg{value} % \end{syntax} % Creates a constant variable with type \meta{type} and sets its value to \meta{value}. % % \fromV{1.1} % \end{function} % % \begin{function}{\object_newconst_seq_from_clist:nnn, \object_newconst_seq_from_clist:Vnn} % \begin{syntax} % \cs{object_newconst_seq_from_clist:nnn} \Arg{address} \Arg{constant name} \Arg{comma-list} % \end{syntax} % Creates a |seq| constant which is set to contain all the items in \meta{comma-list}. % % \fromV{1.1} % \end{function} % % \begin{function}{\object_newconst_prop_from_keyval:nnn, \object_newconst_prop_from_keyval:Vnn} % \begin{syntax} % \cs{object_newconst_prop_from_keyval:nnn} \Arg{address} \Arg{constant name} % \{ % \meta{key} = \meta{value}, ... % \} % \end{syntax} % Creates a |prop| constant which is set to contain all the specified key-value pairs. % % \fromV{1.1} % \end{function} % % \begin{function}{\object_newconst:nnnn} % \begin{syntax} % \cs{object_newconst:nnnn} \Arg{address} \Arg{constant name} \Arg{type} \Arg{value} % \end{syntax} % Invokes \cs{\meta{type}_const:cn} to create the specified constant. % % \fromV{2.1} % \end{function} % % \subsection{Macros} % \begin{function}[rEXP]{\object_macro_adr:nn, \object_macro_adr:Vn} % \begin{syntax} % \cs{object_macro_adr:nn} \Arg{address} \Arg{macro name} % \end{syntax} % Address of specified macro. % % \fromV{2.2} % \end{function} % % \begin{function}[EXP]{\object_macro_use:nn, \object_macro_use:Vn} % \begin{syntax} % \cs{object_macro_use:nn} \Arg{address} \Arg{macro name} % \end{syntax} % Uses the specified macro. This function is expandable if and only if the specified macro is it. % % \fromV{2.2} % \end{function} % % There isn't any standard function to create macros, and macro declarations can't be inserted in a |proxy| object. In fact a macro is just an unspecialized control sequence at the disposal of users that usually already know how to implement them. % % \subsection{Proxies and object creation} % % \begin{function}[pTF]{\object_if_proxy:n, \object_if_proxy:V} % \begin{syntax} % \cs{object_if_proxy_p:n} \marg{address} % \cs{object_if_proxy:nTF} \marg{address} \marg{true code} \marg{false code} % \end{syntax} % Test if the specified object is a proxy object. % % \fromV{1.0} % \end{function} % % \begin{function}[pTF]{\object_test_proxy:nn, \object_test_proxy:Vn} % \begin{syntax} % \cs{object_test_proxy_p:nn} \Arg{object address} \Arg{proxy address} % \cs{object_test_proxy:nnTF} \Arg{object address} \Arg{proxy address} \Arg{true code} \Arg{false code} % \end{syntax} % Test if the specified object is generated by the selected proxy, where \meta{proxy variable} is a string variable holding the proxy address. % % \begin{texnote} % Remember that this command uses internally an |e| expansion so in older engines (any different from Lua\LaTeX\ before 2019) it'll require slow processing. Don't use it in speed critical parts, instead use \cs{object_test_proxy:nN}. % \end{texnote} % % \fromV{2.0} % \end{function} % % \begin{function}[pTF]{\object_test_proxy:nN, \object_test_proxy:VN} % \begin{syntax} % \cs{object_test_proxy_p:nN} \Arg{object address} \meta{proxy variable} % \cs{object_test_proxy:nNTF} \Arg{object address} \meta{proxy variable} \Arg{true code} \Arg{false code} % \end{syntax} % Test if the specified object is generated by the selected proxy, where \meta{proxy variable} is a string variable holding the proxy address. The |:nN| variant don't use |e| expansion, instead of |:nn| command, so it can be safetly used with older compilers. % % \fromV{2.0} % \end{function} % % \begin{variable}{\c_proxy_address_str} % The address of the |proxy| object in the |rawobjects| module. % % \fromV{1.0} % \end{variable} % % \begin{function}{\object_create:nnnNN, \object_create:VnnNN} % \begin{syntax} % \cs{object_create:nnnNN} \marg{proxy address} \marg{module} \marg{id} \meta{scope} \meta{visibility} % \end{syntax} % Creates an object by using the proxy at \meta{proxy address} and the specified parameters. Use this function only if you need to create private objects (at present private objects are functionally equivalent to public objects) or if you need to compile your project with an old version of this library (|< 2.3|). % % \fromV{1.0} % \end{function} % % \begin{function}{\object_create:nnnN, \object_create:VnnN, \object_create:nnn, \object_create:Vnn} % \begin{syntax} % \cs{object_create:nnnN} \Arg{proxy address} \Arg{module} \Arg{id} \meta{scope}\\ % \cs{object_create:nnn} \Arg{proxy address} \Arg{module} \Arg{id} % \end{syntax} % Same as \cs{object_create:nnnNN} but both create only public objects, and the |:nnn| version only global ones. Always use these two function instead of \cs{object_create:nnnNN} unless you strictly need private objects. % % \fromV{2.3} % \end{function} % % \begin{function}{\embedded_create:nnn, \embedded_create:Vnn, \embedded_create:nvn} % \begin{syntax} % \cs{embedded_create:nnn} \Arg{parent object} \Arg{proxy address} \Arg{id} % \end{syntax} % Creates an embedded object with name \meta{id} inside \meta{parent object}. % % \fromV{2.2} % \end{function} % % \begin{variable}{\c_object_local_str, \c_object_global_str} % Possible values for \meta{scope} parameter. % % \fromV{1.0} % \end{variable} % % \begin{variable}{\c_object_public_str, \c_object_private_str} % Possible values for \meta{visibility} parameter. % % \fromV{1.0} % \end{variable} % % \begin{function}{\object_create_set:NnnnNN, \object_create_set:NVnnNN, \object_create_set:NnnfNN, \object_create_gset:NnnnNN, \object_create_gset:NVnnNN, \object_create_gset:NnnfNN} % \begin{syntax} % \cs{object_create_set:NnnnNN} \meta{str var} \marg{proxy address} \marg{module} \marg{id} \meta{scope} \meta{visibility} % \end{syntax} % Creates an object and sets its fully expanded address inside \meta{str var}. % % \fromV{1.0} % \end{function} % % \begin{function}{\object_allocate_incr:NNnnNN, \object_allocate_incr:NNVnNN, \object_gallocate_incr:NNnnNN, \object_gallocate_incr:NNVnNN, \object_allocate_gincr:NNnnNN, \object_allocate_gincr:NNVnNN, \object_gallocate_gincr:NNnnNN, \object_gallocate_gincr:NNVnNN} % \begin{syntax} % \cs{object_allocate_incr:NNnnNN} \meta{str var} \meta{int var} \Arg{proxy address} \Arg{module} \meta{scope} \meta{visibility} % \end{syntax} % Build a new object address with module \meta{module} and an identifier generated from \meta{proxy address} and the integer contained inside \meta{int var}, then increments \meta{int var}. This is very useful when you need to create a lot of objects, each of them on a different address. the |_incr| version increases \meta{int var} locally whereas |_gincr| does it globally. % % \fromV{1.1} % \end{function} % % \begin{function}{\proxy_create:nnN, \proxy_create_set:NnnN, \proxy_create_gset:NnnN} % \begin{syntax} % \cs{proxy_create:nnN} \Arg{module} \Arg{id} \meta{visibility} % \cs{proxy_create_set:NnnN} \meta{str var} \Arg{module} \Arg{id} \meta{visibility} % \end{syntax} % These commands are deprecated because proxies should be global and public. Use instead \cs{proxy_create:nn}, \cs{proxy_create_set:Nnn} and \cs{proxy_create_gset:Nnn}. % % \fromVD{1.0}{2.3} % \end{function} % % \begin{function}{\proxy_create:nn, \proxy_create_set:Nnn, \proxy_create_gset:Nnn} % \begin{syntax} % \cs{proxy_create:nn} \Arg{module} \Arg{id} % \cs{proxy_create_set:Nnn} \meta{str var} \Arg{module} \Arg{id} % \end{syntax} % Creates a global public proxy object. % % \fromV{2.3} % \end{function} % % \begin{function}{\proxy_push_member:nnn, \proxy_push_member:Vnn} % \begin{syntax} % \cs{proxy_push_member:nnn} \Arg{proxy address} \Arg{member name} \Arg{member type} % \end{syntax} % Updates a proxy object with a new member specification, so that every subsequential object created with this proxy will have a member variable with the specified name and type that can be retrieved with \cs{object_member_type} functions. % % \fromV{1.0} % \end{function} % % \begin{function}{\proxy_push_embedded:nnn, \proxy_push_embedded:Vnn} % \begin{syntax} % \cs{proxy_push_embedded:nnn} \Arg{proxy address} \Arg{embedded object name} \Arg{embedded object proxy} % \end{syntax} % Updates a proxy object with a new embedded object specification. % % \fromV{2.2} % \end{function} % % \begin{function}{\proxy_add_initializer:nN, \proxy_add_initializer:VN} % \begin{syntax} % \cs{proxy_add_initializer:nN} \Arg{proxy address} \meta{initializer} % \end{syntax} % Pushes a new initializer that will be executed on each created objects. An initializer is a function that should accept five arguments in this order: % \begin{itemize} % \item the full expanded address of used proxy as an |n| argument; % \item the module name as an |n| argument; % \item the full expanded address of created object as an |n| argument. % \end{itemize} % % Initializer will be executed in the same order they're added. % % \fromV{2.3} % \end{function} % % \begin{function}{\object_assign:nn, \object_assign:Vn, \object_assign:nV, \object_assign:VV} % \begin{syntax} % \cs{object_assign:nn} \marg{to address} \marg{from address} % \end{syntax} % Assigns the content of each variable of object at \meta{from address} to each correspective variable in \meta{to address}. Both the objects should be created with the same proxy object and only variables listed in the proxy are assigned. % % \fromV{1.0} % \end{function} % % \section{Examples} % \subsection*{Example 1} % Create a public proxy with id \verb|myproxy| with the specification of a single member variable with name \verb|myvar| and type \verb|tl|, then set its address inside \cs{g_myproxy_str}. % % \begin{Coding} % !simfun[str_new][N] !simvar[g_myproxy_str] % !simfun[proxy_create_gset][Nnn] !simvar[g_myproxy_str] { example }{ myproxy } % !simfun[proxy_push_member][Vnn] !simvar[g_myproxy_str] { myvar }{ tl } % \end{Coding} % % Then create a new object with name \verb|myobj| with that proxy, assign then token list \verb|\c_dollar_str{} ~ dollar ~ \c_dollar_str{}| to \verb|myvar| and then print it. % % \begin{Coding} % !simfun[str_new][N] !simvar[g_myobj_str] % !simfun[object_create_gset][NVnn] !simvar[g_myobj_str] !simvar[g_myproxy_str] % { example }{ myobj } % !simfun[tl_gset][cn] % { % !simfun[object_member_adr][Vn] !simvar[g_myobj_str] { myvar } % } % { !simvar[c_dollar_str]{} ~ dollar ~ !simvar[c_dollar_str]{} } % !simfun[object_member_use][Vn] !simvar[g_myobj_str] { myvar } % \end{Coding} % % Output: \ExplSyntaxOn % \str_new:N \g_myproxy_str % \proxy_create_gset:Nnn \g_myproxy_str { example }{ myproxy } % \proxy_push_member:Vnn \g_myproxy_str { myvar }{ tl } % \str_new:N \g_myobj_str % \object_create_gset:NVnn \g_myobj_str \g_myproxy_str % { example }{ myobj } % \tl_gset:cn % { % \object_member_adr:Vn \g_myobj_str { myvar } % } % { \c_dollar_str{} ~ dollar ~ \c_dollar_str{} } % \object_member_use:Vn \g_myobj_str { myvar } % \ExplSyntaxOff % % You can also avoid to specify an object identify and use \cs{object_gallocate_gincr} instead: % % \begin{Coding} % !simfun[int_new][N] !simvar[g_intc_int] % !simfun[object_gallocate_gincr][NNVnNN] !simvar[g_myobj_str] !simvar[g_intc_int] !simvar[g_myproxy_str] % { example } !simvar[c_object_local_str] !simvar[c_object_public_str] % !simfun[tl_gset][cn] % { % !simfun[object_member_adr][Vn] !simvar[g_myobj_str] { myvar } % } % { !simvar[c_dollar_str]{} ~ dollar ~ !simvar[c_dollar_str]{} } % !simfun[object_member_use][Vn] !simvar[g_myobj_str] { myvar } % \end{Coding} % % Output: \ExplSyntaxOn % \int_new:N \g_intc_int % \object_gallocate_gincr:NNVnNN \g_myobj_str \g_intc_int \g_myproxy_str % { example } \c_object_local_str \c_object_public_str % \tl_gset:cn % { % \object_member_adr:Vn \g_myobj_str { myvar } % } % { \c_dollar_str{} ~ dollar ~ \c_dollar_str{} } % \object_member_use:Vn \g_myobj_str { myvar } % \ExplSyntaxOff % % \subsection*{Example 2} % In this example we create a proxy object with an embedded object inside. % % Internal proxy % \begin{Coding} % !simfun[proxy_create][nn] { mymod }{ INT } % !simfun[proxy_push_member][nnn] % { % !simfun[object_address][nn] { mymod }{ INT } % }{ var }{ tl } % \end{Coding} % % Container proxy % \begin{Coding} % !simfun[proxy_create][nn] { mymod }{ EXT } % !simfun[proxy_push_embedded][nnn] % { % !simfun[object_address][nn] { mymod }{ EXT } % } % { emb } % { % !simfun[object_address][nn] { mymod }{ INT } % } % \end{Coding} % % Now we create a new object from proxy |EXT|. It'll contain an embedded object created with |INT| proxy: % \begin{Coding} % !simfun[str_new][N] !simvar[g_EXTobj_str] % !simfun[int_new][N] !simvar[g_intcount_int] % !simfun[object_gallocate_gincr][NNnnNN] % !simvar[g_EXTobj_str] !simvar[g_intcount_int] % { % !simfun[object_address][nn] { mymod }{ EXT } % } % { mymod } % !simvar[c_object_local_str] !simvar[c_object_public_str] % \end{Coding} % and use the embedded object in the following way: % \begin{Coding} % !simfun[object_member_set][nnn] % { % !simfun[object_embedded_adr][Vn] !simvar[g_EXTobj_str] { emb } % }{ var }{ Hi } % !simfun[object_member_use][nn] % { % !simfun[object_embedded_adr][Vn] !simvar[g_EXTobj_str] { emb } % }{ var } % \end{Coding} % Output: \ExplSyntaxOn % \proxy_create:nn{ mymod }{ INT } % \proxy_push_member:nnn % { % \object_address:nn{ mymod }{ INT } % }{ var }{ tl } % % \proxy_create:nn{ mymod }{ EXT } % \proxy_push_embedded:nnn % { % \object_address:nn{ mymod }{ EXT } % } % { emb } % { % \object_address:nn{ mymod }{ INT } % } % % \str_new:N \g_EXTobj_str % \int_new:N \g_intcount_int % \object_gallocate_gincr:NNnnNN % \g_EXTobj_str \g_intcount_int % { % \object_address:nn{ mymod }{ EXT } % } % { mymod } % \c_object_local_str \c_object_public_str % % \object_member_set:nnn % { % \object_embedded_adr:Vn \g_EXTobj_str { emb } % }{ var }{ Hi } % \object_member_use:nn % { % \object_embedded_adr:Vn \g_EXTobj_str { emb } % }{ var } % \ExplSyntaxOff % % \subsection*{Example 3} % Here we show how to properly use \cs{object_member_generate:NN}. Suppose we don't know \cs{object_member_use} and we want to use \cs{tl_use:N} to get the value stored in member |MEM| of object |U| in module |MD3|. % % \ExplSyntaxOn % \proxy_create:nn {MD3}{ex3p} % \proxy_push_member:nnn { \object_address:nn {MD3}{ex3p} }{MEM}{tl} % \object_create:nnn { \object_address:nn {MD3}{ex3p} }{MD3}{U} % \ExplSyntaxOff % % We can do it in this way: % \begin{Coding} % !simfun[tl_use][c] % { % !simfun[object_member_adr][nnn] % { !simfun[object_address][nn] { MD3 }{ U } } % { MEM }{ tl } % } % \end{Coding} % but this solution is not so pratical since we should write a lot of code each time. We can then use \cs{object_member_generate:NN} to define an auxilary macro \cs{myaux_print_tl:nnn} in this way: % \begin{Coding} % !simfun[object_member_generate][NN] \myaux_print_tl !simfun[tl_use][c] % \end{Coding} % then we can get the content of our member in this way: % \begin{Coding} % !simfun[myaux_print_tl][nnn] % { !simfun[object_address][nn] { MD3 }{ U } } % { MEM }{ tl } % \end{Coding} % % For example if |U| contains |Hi| then the preceding code will output \ExplSyntaxOn % \object_member_set:nnnn % { \object_address:nn {MD3}{U}}{MEM}{tl}{Hi} % \object_member_generate_inline:Nnn \myaux_print_tl { #1_use }{ c } % \myaux_print_tl:nn % { \object_address:nn {MD3}{U}}{MEM} % \ExplSyntaxOff . If member |MEM| is tracked then you can use also the following command, which is generated together with \cs{myaux_print_tl:nnn} % \begin{Coding} % !simfun[myaux_print_tl][nn] % { !simfun[object_address][nn] { MD3 }{ U } } % { MEM } % \end{Coding} % % However, this function only works with |tl| members since we use \cs{tl_use:N}, so you should define a new function for every possible type, and even if you do it newer types introduced in other packages will not be supported. In such cases you can use \cs{object_member_generate_inline:Nnn} which allows you to build the called function by specifying its name and its parameters. The preceding code then becomes % \begin{Coding} % !simfun[object_member_generate_inline][Nnn] \myaux_print_tl { tl_use }{ c } % \end{Coding} % % This function does much more: in the second argument you can put also the parameters |#1| and |#2| that will expand respectively to the type of specified member and its scope. Let \cs{myaux_print:nnn} be our version of \cs{object_member_use:nnn} that retrieves the valued of the specified member, we are now able to define it in this way: % \begin{Coding} % !simfun[object_member_generate_inline][Nnn] \myaux_print { #1_use }{ c } % \end{Coding} % % When you use \cs{myaux_print:nnn} on a member of type |int| it replaces all the recurrences of |#1| with |int|, thus it will call \cs{int_use:c}. %\end{documentation} % %\begin{implementation} % %\section{Implementation} % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=rawobjects> % \end{macrocode} % % Deprecation message % \begin{macrocode} \msg_new:nnn { rawobjects }{ deprecate } { Command ~ #1 ~ is ~ deprecated. ~ Use ~ instead ~ #2 } \cs_new_protected:Nn \@@_launch_deprecate:NN { \msg_warning:nnnn{ rawobjects }{ deprecate }{ #1 }{ #2 } } % \end{macrocode} % % \begin{macro}{\rwobj_address_f:n} % It just performs a |c| expansion before passing it to \cs{cs_to_str:N}. % \begin{macrocode} \cs_new:Nn \rwobj_address_f:n { \exp_args:Nc \cs_to_str:N { #1 } } % \end{macrocode} % \end{macro} % %\begin{variable}{\c_object_local_str, \c_object_global_str, \c_object_public_str, \c_object_private_str} % \begin{macrocode} \str_const:Nn \c_object_local_str {l} \str_const:Nn \c_object_global_str {g} \str_const:Nn \c_object_public_str {_} \str_const:Nn \c_object_private_str {__} \cs_new:Nn \@@_scope:N { \str_use:N #1 } \cs_new:Nn \@@_scope_pfx:N { \str_if_eq:NNF #1 \c_object_local_str { g } } \cs_generate_variant:Nn \@@_scope_pfx:N { c } \cs_new:Nn \@@_scope_pfx_cl:n { \@@_scope_pfx:c{ \object_ncmember_adr:nnn { \object_embedded_adr:nn { #1 }{ /_I_/ } } { S }{ str } } } \cs_new:Nn \@@_vis_var:N { \str_use:N #1 } \cs_new:Nn \@@_vis_fun:N { \str_if_eq:NNT #1 \c_object_private_str { __ } } % \end{macrocode} %\end{variable} % %\begin{macro}{\object_address:nn} %Get address of an object % \begin{macrocode} \cs_new:Nn \object_address:nn { \tl_to_str:n { #1 _ #2 } } % \end{macrocode} %\end{macro} % % \begin{macro}{\object_embedded_adr:nn} % Address of embedded object % \begin{macrocode} \cs_new:Nn \object_embedded_adr:nn { #1 \tl_to_str:n{ _SUB_ #2 } } \cs_generate_variant:Nn \object_embedded_adr:nn{ Vn } % \end{macrocode} % \end{macro} % %\begin{macro}{\object_address_set:Nnn, \object_address_gset:Nnn} % Saves the address of an object into a string variable % \begin{macrocode} \cs_new_protected:Nn \object_address_set:Nnn { \str_set:Nn #1 { #2 _ #3 } } \cs_new_protected:Nn \object_address_gset:Nnn { \str_gset:Nn #1 { #2 _ #3 } } % \end{macrocode} %\end{macro} % %\begin{macro}[pTF]{\object_if_exist:n} %Tests if object exists. % \begin{macrocode} \prg_new_conditional:Nnn \object_if_exist:n { p, T, F, TF } { \cs_if_exist:cTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { S }{ str } } { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \object_if_exist:n { V } { p, T, F, TF } % \end{macrocode} %\end{macro} % %\begin{macro}{\object_get_module:n, \object_get_proxy_adr:n} %Retrieve the name, module and generating proxy of an object % \begin{macrocode} \cs_new:Nn \object_get_module:n { \object_ncmember_use:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { M }{ str } } \cs_new:Nn \object_get_proxy_adr:n { \object_ncmember_use:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { P }{ str } } \cs_generate_variant:Nn \object_get_module:n { V } \cs_generate_variant:Nn \object_get_proxy_adr:n { V } % \end{macrocode} %\end{macro} % %\begin{macro}[pTF]{\object_if_local:n, \object_if_global:n, \object_if_public:n, \object_if_private:n} %Test the specified parameters. % \begin{macrocode} \prg_new_conditional:Nnn \object_if_local:n {p, T, F, TF} { \str_if_eq:cNTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { S }{ str } } \c_object_local_str { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \object_if_global:n {p, T, F, TF} { \str_if_eq:cNTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { S }{ str } } \c_object_global_str { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \object_if_public:n {p, T, F, TF} { \str_if_eq:cNTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { V }{ str } } \c_object_public_str { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \object_if_private:n {p, T, F, TF} { \str_if_eq:cNTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { V }{ str } } \c_object_private_str { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \object_if_local:n { V } { p, T, F, TF } \prg_generate_conditional_variant:Nnn \object_if_global:n { V } { p, T, F, TF } \prg_generate_conditional_variant:Nnn \object_if_public:n { V } { p, T, F, TF } \prg_generate_conditional_variant:Nnn \object_if_private:n { V } { p, T, F, TF } % \end{macrocode} %\end{macro} % %\begin{macro}{\object_macro_adr:nn, \object_macro_use:nn} % Generic macro address % \begin{macrocode} \cs_new:Nn \object_macro_adr:nn { #1 \tl_to_str:n{ _MACRO_ #2 } } \cs_generate_variant:Nn \object_macro_adr:nn{ Vn } \cs_new:Nn \object_macro_use:nn { \use:c { \object_macro_adr:nn{ #1 }{ #2 } } } \cs_generate_variant:Nn \object_macro_use:nn{ Vn } % \end{macrocode} % \end{macro} % %\begin{macro}{\@@_member_adr:nnnNN} % Macro address without object inference % \begin{macrocode} \cs_new:Nn \@@_member_adr:nnnNN { \@@_scope:N #4 \@@_vis_var:N #5 #1 \tl_to_str:n { _ MEMBER _ #2 _ #3 } } \cs_generate_variant:Nn \@@_member_adr:nnnNN { VnnNN, nnncc } % \end{macrocode} % \end{macro} % %\begin{macro}{\object_member_adr:nnn} %Get the address of a member variable % \begin{macrocode} \cs_new:Nn \object_member_adr:nnn { \@@_member_adr:nnncc { #1 }{ #2 }{ #3 } { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { S }{ str } } { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { V }{ str } } } \cs_generate_variant:Nn \object_member_adr:nnn { Vnn, vnn, nnv, nnf } % \end{macrocode} %\end{macro} % % \begin{macro}[pTF]{\object_member_if_exist:nnn} % Tests if the specified member exists % \begin{macrocode} \prg_new_conditional:Nnn \object_member_if_exist:nnn {p, T, F, TF } { \cs_if_exist:cTF { \object_member_adr:nnn { #1 }{ #2 }{ #3 } } { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \object_member_if_exist:nnn { Vnn }{ p, T, F, TF } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\object_member_if_tracked:nn} % Tests if the member is tracked. % \begin{macrocode} \prg_new_conditional:Nnn \object_member_if_tracked:nn {p, T, F, TF } { \cs_if_exist:cTF { \object_rcmember_adr:nnn { #1 }{ #2 _ type }{ str } } { \prg_return_true: } { \cs_if_exist:cTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn { #1 }{ /_T_/ } } { #2 _ type }{ str } } { \prg_return_true: } { \prg_return_false: } } } \prg_generate_conditional_variant:Nnn \object_member_if_tracked:nn { Vn }{ p, T, F, TF } \prg_new_eq_conditional:NNn \object_member_if_exist:nn \object_member_if_tracked:nn { p, T, F, TF } \prg_new_eq_conditional:NNn \object_member_if_exist:Vn \object_member_if_tracked:Vn { p, T, F, TF } % \end{macrocode} % \end{macro} % % \begin{macro}{\object_member_type:nn} % Deduce the type of tracked members. % \begin{macrocode} \cs_new:Nn \object_member_type:nn { \cs_if_exist:cTF { \object_rcmember_adr:nnn { #1 }{ #2 _ type }{ str } } { \object_rcmember_use:nnn { #1 }{ #2 _ type }{ str } } { \cs_if_exist:cT { \object_ncmember_adr:nnn { \object_embedded_adr:nn { #1 }{ /_T_/ } } { #2 _ type }{ str } } { \object_ncmember_use:nnn { \object_embedded_adr:nn { #1 }{ /_T_/ } } { #2 _ type }{ str } } } } % \end{macrocode} % \end{macro} % %\begin{macro}{\object_member_adr:nn} %Get the address of a member variable % \begin{macrocode} \cs_new:Nn \object_member_adr:nn { \object_member_adr:nnf { #1 }{ #2 } { \object_member_type:nn { #1 }{ #2 } } } \cs_generate_variant:Nn \object_member_adr:nn { Vn } % \end{macrocode} %\end{macro} % % Helper functions for \cs{object_*_generate} functions. % \begin{macrocode} \cs_new:Nn \@@_par_trans:N { \str_case:nnF { #1 } { { N }{ N } { V }{ N } { n }{ n } { v }{ n } { f }{ n } { x }{ n } { e }{ n } { o }{ n } { ~ }{} } { #1 } } \cs_new:Nn \@@_par_trans:n { \str_map_function:nN { #1 } \@@_par_trans:N } \str_new:N \l_@@_tmp_fa_str \cs_new_protected:Nn \@@_save_dat:n { \str_set:Nx \l_@@_tmp_fa_str { \str_tail:n{ #1 } } } \cs_new_protected:Nn \@@_save_dat:nnN { \str_set:Nx \l_@@_tmp_fa_str { \str_tail:n{ #2 } } } \cs_new_protected:Nn \@@_save_dat_aux:n { \@@_save_dat:nnN #1 } \cs_generate_variant:Nn \@@_save_dat_aux:n { f } \cs_new_protected:Nn \@@_save_fun:N { \@@_save_dat_aux:f { \cs_split_function:N #1 } } \cs_new:Nn \@@_use_dat:nn { #1 : #2 \str_use:N \l_@@_tmp_fa_str } % \end{macrocode} % % \begin{macro}{\object_member_generate:NN, \object_member_generate_inline:Nnn, \object_member_generate_protected:NN, \object_member_generate_protected_inline:Nnn} % Generate member versions of specified functions. % \begin{macrocode} \cs_new_protected:Nn \@@_mgen:nN { \@@_save_fun:N #2 \cs_new:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { #2 { \object_member_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } \cs_new:cpn { #1 : nn \str_use:N \l_@@_tmp_fa_str } ##1##2 { #2 { \object_member_adr:nn{ ##1 }{ ##2 } } } } \cs_new_protected:Nn \@@_mgen_pr:nN { \@@_save_fun:N #2 \cs_new_protected:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { #2 { \object_member_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } \cs_new_protected:cpn { #1 : nn \str_use:N \l_@@_tmp_fa_str } ##1##2 { #2 { \object_member_adr:nn{ ##1 }{ ##2 } } } } \cs_new_protected:Nn \@@_mgen:nnn { \@@_save_dat:n { #3 } \cs_new:cpn { @@_auxfun_#1 :nn } ##1##2 { \use:c{ #2 : #3 } } \cs_generate_variant:cn { @@_auxfun_#1 :nn }{ nf, ff } \cs_new:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { \use:c { @@_auxfun_#1 :nf } { ##3 } { \@@_scope_pfx_cl:n{ ##1 } } { \object_member_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } \cs_new:cpn { #1 : nn \str_use:N \l_@@_tmp_fa_str } ##1##2 { \use:c { @@_auxfun_#1 :ff } { \object_member_type:nn { ##1 }{ ##2 } } { \@@_scope_pfx_cl:n{ ##1 } } { \object_member_adr:nn{ ##1 }{ ##2 } } } } \cs_new_protected:Nn \@@_mgen_pr:nnn { \@@_save_dat:n { #3 } \cs_new:cpn { @@_auxfun_#1 :nn } ##1##2 { \use:c{ #2 : #3 } } \cs_generate_variant:cn { @@_auxfun_#1 :nn }{ nf, ff } \cs_new_protected:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { \use:c { @@_auxfun_#1 :nf } { ##3 } { \@@_scope_pfx_cl:n{ ##1 } } { \object_member_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } \cs_new_protected:cpn { #1 : nn \str_use:N \l_@@_tmp_fa_str } ##1##2 { \use:c { @@_auxfun_#1 :ff } { \object_member_type:nn { ##1 }{ ##2 } } { \@@_scope_pfx_cl:n{ ##1 } } { \object_member_adr:nn{ ##1 }{ ##2 } } } } \cs_generate_variant:Nn \@@_mgen:nN { fN } \cs_generate_variant:Nn \@@_mgen:nnn { fnn } \cs_generate_variant:Nn \@@_mgen_pr:nN { fN } \cs_generate_variant:Nn \@@_mgen_pr:nnn { fnn } \cs_new_protected:Nn \object_member_generate:NN { \@@_mgen:fN { \cs_to_str:N #1 } #2 } \cs_new_protected:Nn \object_member_generate_inline:Nnn { \@@_mgen:fnn { \cs_to_str:N #1 }{ #2 }{ #3 } } \cs_new_protected:Nn \object_member_generate_protected:NN { \@@_mgen_pr:fN { \cs_to_str:N #1 } #2 } \cs_new_protected:Nn \object_member_generate_protected_inline:Nnn { \@@_mgen_pr:fnn { \cs_to_str:N #1 }{ #2 }{ #3 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\object_ncmember_generate:NN, \object_ncmember_generate_inline:Nnn, \object_ncmember_generate_protected:NN, \object_ncmember_generate_protected_inline:Nnn} % Generate ncmember versions of specified functions. % \begin{macrocode} \cs_new_protected:Nn \@@_ncgen:nN { \@@_save_fun:N #2 \cs_new:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { #2 { \object_ncmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_new_protected:Nn \@@_ncgen_pr:nN { \@@_save_fun:N #2 \cs_new_protected:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { #2 { \object_ncmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_new_protected:Nn \@@_ncgen:nnn { \@@_save_dat:n { #3 } \cs_new:cpn { @@_auxfun_#1 :nn } ##1##2 { \use:c{ #2 : #3 } } \cs_generate_variant:cn { @@_auxfun_#1 :nn }{ nf } \cs_new:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { \use:c { @@_auxfun_#1 :nf } { ##3 } { \@@_scope_pfx_cl:n{ ##1 } } { \object_ncmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_new_protected:Nn \@@_ncgen_pr:nnn { \@@_save_dat:n { #3 } \cs_new:cpn { @@_auxfun_#1 :nn } ##1##2 { \use:c{ #2 : #3 } } \cs_generate_variant:cn { @@_auxfun_#1 :nn }{ nf } \cs_new_protected:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { \use:c { @@_auxfun_#1 :nf } { ##3 } { \@@_scope_pfx_cl:n{ ##1 } } { \object_ncmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_generate_variant:Nn \@@_ncgen:nN { fN } \cs_generate_variant:Nn \@@_ncgen:nnn { fnn } \cs_generate_variant:Nn \@@_ncgen_pr:nN { fN } \cs_generate_variant:Nn \@@_ncgen_pr:nnn { fnn } \cs_new_protected:Nn \object_ncmember_generate:NN { \@@_ncgen:fN { \cs_to_str:N #1 } #2 } \cs_new_protected:Nn \object_ncmember_generate_inline:Nnn { \@@_ncgen:fnn { \cs_to_str:N #1 }{ #2 }{ #3 } } \cs_new_protected:Nn \object_ncmember_generate_protected:NN { \@@_ncgen_pr:fN { \cs_to_str:N #1 } #2 } \cs_new_protected:Nn \object_ncmember_generate_protected_inline:Nnn { \@@_ncgen_pr:fnn { \cs_to_str:N #1 }{ #2 }{ #3 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\object_rcmember_generate:NN, \object_rcmember_generate_inline:Nnn, \object_rcmember_generate_protected:NN, \object_rcmember_generate_protected_inline:Nnn} % Generate ncmember versions of specified functions. % \begin{macrocode} \cs_new_protected:Nn \@@_rcgen:nN { \@@_save_fun:N #2 \cs_new:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { #2 { \object_rcmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_new_protected:Nn \@@_rcgen_pr:nN { \@@_save_fun:N #2 \cs_new_protected:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { #2 { \object_rcmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_new_protected:Nn \@@_rcgen:nnn { \@@_save_dat:n { #3 } \cs_new:cpn { @@_auxfun_#1 :nn } ##1##2 { \use:c{ #2 : #3 } } \cs_generate_variant:cn { @@_auxfun_#1 :nn }{ nf } \cs_new:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { \use:c { @@_auxfun_#1 :nf } { ##3 } { \@@_scope_pfx_cl:n{ ##1 } } { \object_rcmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_new_protected:Nn \@@_rcgen_pr:nnn { \@@_save_dat:n { #3 } \cs_new:cpn { @@_auxfun_#1 :nn } ##1##2 { \use:c{ #2 : #3 } } \cs_generate_variant:cn { @@_auxfun_#1 :nn }{ nf } \cs_new_protected:cpn { #1 : nnn \str_use:N \l_@@_tmp_fa_str } ##1##2##3 { \use:c { @@_auxfun_#1 :nf } { ##3 } { \@@_scope_pfx_cl:n{ ##1 } } { \object_rcmember_adr:nnn{ ##1 }{ ##2 }{ ##3 } } } } \cs_generate_variant:Nn \@@_rcgen:nN { fN } \cs_generate_variant:Nn \@@_rcgen:nnn { fnn } \cs_generate_variant:Nn \@@_rcgen_pr:nN { fN } \cs_generate_variant:Nn \@@_rcgen_pr:nnn { fnn } \cs_new_protected:Nn \object_rcmember_generate:NN { \@@_rcgen:fN { \cs_to_str:N #1 } #2 } \cs_new_protected:Nn \object_rcmember_generate_inline:Nnn { \@@_rcgen:fnn { \cs_to_str:N #1 }{ #2 }{ #3 } } \cs_new_protected:Nn \object_rcmember_generate_protected:NN { \@@_rcgen_pr:fN { \cs_to_str:N #1 } #2 } \cs_new_protected:Nn \object_rcmember_generate_protected_inline:Nnn { \@@_rcgen_pr:fnn { \cs_to_str:N #1 }{ #2 }{ #3 } } % \end{macrocode} % \end{macro} % % Auxilary functions % \begin{macrocode} \cs_generate_variant:Nn \cs_generate_variant:Nn { cx } \cs_new_protected:Nn \@@_genmem_int:nnn { \@@_mgen:nnn { #1 }{ #2 }{ #3 } \cs_generate_variant:cx { #1 : nnn \str_use:N \l_@@_tmp_fa_str } { Vnn \str_use:N \l_@@_tmp_fa_str, nnv \str_use:N \l_@@_tmp_fa_str } \cs_generate_variant:cx { #1 : nn \str_use:N \l_@@_tmp_fa_str } { Vn \str_use:N \l_@@_tmp_fa_str } } \cs_new_protected:Nn \@@_genmem_pr_int:nnn { \@@_mgen_pr:nnn { #1 }{ #2 }{ #3 } \cs_generate_variant:cx { #1 : nnn \str_use:N \l_@@_tmp_fa_str } { Vnn \str_use:N \l_@@_tmp_fa_str, nnv \str_use:N \l_@@_tmp_fa_str } \cs_generate_variant:cx { #1 : nn \str_use:N \l_@@_tmp_fa_str } { Vn \str_use:N \l_@@_tmp_fa_str } } \cs_new_protected:Nn \@@_genncm_int:nnn { \@@_ncgen:nnn { #1 }{ #2 }{ #3 } \cs_generate_variant:cx { #1 : nnn \str_use:N \l_@@_tmp_fa_str } { Vnn \str_use:N \l_@@_tmp_fa_str } } \cs_new_protected:Nn \@@_genncm_pr_int:nnn { \@@_ncgen_pr:nnn { #1 }{ #2 }{ #3 } \cs_generate_variant:cx { #1 : nnn \str_use:N \l_@@_tmp_fa_str } { Vnn \str_use:N \l_@@_tmp_fa_str } } \cs_new_protected:Nn \@@_genrcm_int:nnn { \@@_rcgen:nnn { #1 }{ #2 }{ #3 } \cs_generate_variant:cx { #1 : nnn \str_use:N \l_@@_tmp_fa_str } { Vnn \str_use:N \l_@@_tmp_fa_str } } \cs_new_protected:Nn \@@_genrcm_pr_int:nnn { \@@_rcgen_pr:nnn { #1 }{ #2 }{ #3 } \cs_generate_variant:cx { #1 : nnn \str_use:N \l_@@_tmp_fa_str } { Vnn \str_use:N \l_@@_tmp_fa_str } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { rawobjects }{ noerr }{ Unspecified ~ scope } { Object ~ #1 ~ hasn't ~ a ~ scope ~ variable } % \end{macrocode} % %\begin{macro}{\object_new_member:nnn, \object_new_member_tracked:nnn} %Creates a new member variable % \begin{macrocode} \@@_genmem_pr_int:nnn { object_new_member }{ #1 _ new }{ c } \cs_new_protected:Nn \object_new_member_tracked:nnn { \object_new_member:nnn { #1 }{ #2 }{ #3 } \str_const:cn { \object_ncmember_adr:nnn { \object_embedded_adr:nn { #1 }{ /_T_/ } } { #2 _ type }{ str } } { #3 } } \cs_generate_variant:Nn \object_new_member_tracked:nnn { Vnn, nnv } % \end{macrocode} %\end{macro} % %\begin{macro}{\object_member_use:nnn, \object_member_use:nn} %Uses a member variable % \begin{macrocode} \@@_genmem_int:nnn {object_member_use}{ #1_use }{c} \cs_generate_variant:Nn \object_member_use:nnn {vnn} % \end{macrocode} %\end{macro} % %\begin{macro}{\object_member_set:nnnn, \object_member_set:nnn} % Set the value a member. % \begin{macrocode} \@@_genmem_pr_int:nnn {object_member_set}{ #1_#2 set }{ cn } % \end{macrocode} %\end{macro} % %\begin{macro}{\object_member_set_eq:nnnN, \object_member_set_eq:nnN} % Make a member equal to another variable. % \begin{macrocode} \@@_genmem_pr_int:nnn { object_member_set_eq }{ #1 _ #2 set_eq }{ cN } \cs_generate_variant:Nn \object_member_set_eq:nnnN { nnnc, Vnnc } \cs_generate_variant:Nn \object_member_set_eq:nnN { nnc, Vnc } % \end{macrocode} %\end{macro} % % \begin{macro}{\object_ncmember_adr:nnn} % Get address of near constant % \begin{macrocode} \cs_new:Nn \object_ncmember_adr:nnn { \tl_to_str:n{ c _ } #1 \tl_to_str:n { _ CONST _ #2 _ #3 } } \cs_generate_variant:Nn \object_ncmember_adr:nnn { Vnn, vnn } % \end{macrocode} % \end{macro} % %\begin{macro}{\object_rcmember_adr:nnn} % Get the address of a remote constant. % \begin{macrocode} \cs_new:Nn \object_rcmember_adr:nnn { \object_ncmember_adr:vnn { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { P }{ str } } { #2 }{ #3 } } \cs_generate_variant:Nn \object_rcmember_adr:nnn { Vnn } % \end{macrocode} %\end{macro} % % \begin{macro}[pTF]{\object_ncmember_if_exist:nnn, \object_rcmember_if_exist:nnn} % Tests if the specified member constant exists. % \begin{macrocode} \prg_new_conditional:Nnn \object_ncmember_if_exist:nnn {p, T, F, TF } { \cs_if_exist:cTF { \object_ncmember_adr:nnn { #1 }{ #2 }{ #3 } } { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \object_rcmember_if_exist:nnn {p, T, F, TF } { \cs_if_exist:cTF { \object_rcmember_adr:nnn { #1 }{ #2 }{ #3 } } { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \object_ncmember_if_exist:nnn { Vnn }{ p, T, F, TF } \prg_generate_conditional_variant:Nnn \object_rcmember_if_exist:nnn { Vnn }{ p, T, F, TF } % \end{macrocode} % \end{macro} % %\begin{macro}{\object_ncmember_use:nnn, \object_rcmember_use:nnn} % Uses a near/remote constant. % \begin{macrocode} \@@_genncm_int:nnn { object_ncmember_use }{ #1_use}{ c } \@@_genrcm_int:nnn { object_rcmember_use }{ #1_use}{ c } % \end{macrocode} %\end{macro} % % \begin{macro}{\object_newconst:nnnn} % Creates a constant variable, use with caution % \begin{macrocode} \@@_genncm_pr_int:nnn { object_newconst }{ #1 _ const }{ cn } % \end{macrocode} % \end{macro} % % \begin{macro}{\object_newconst_tl:nnn, \object_newconst_str:nnn, \object_newconst_int:nnn, \object_newconst_clist:nnn, \object_newconst_dim:nnn, \object_newconst_skip:nnn, \object_newconst_fp:nnn} % Create constants % \begin{macrocode} \cs_new_protected:Nn \object_newconst_tl:nnn { \object_newconst:nnnn { #1 }{ #2 }{ tl }{ #3 } } \cs_new_protected:Nn \object_newconst_str:nnn { \object_newconst:nnnn { #1 }{ #2 }{ str }{ #3 } } \cs_new_protected:Nn \object_newconst_int:nnn { \object_newconst:nnnn { #1 }{ #2 }{ int }{ #3 } } \cs_new_protected:Nn \object_newconst_clist:nnn { \object_newconst:nnnn { #1 }{ #2 }{ clist }{ #3 } } \cs_new_protected:Nn \object_newconst_dim:nnn { \object_newconst:nnnn { #1 }{ #2 }{ dim }{ #3 } } \cs_new_protected:Nn \object_newconst_skip:nnn { \object_newconst:nnnn { #1 }{ #2 }{ skip }{ #3 } } \cs_new_protected:Nn \object_newconst_fp:nnn { \object_newconst:nnnn { #1 }{ #2 }{ fp }{ #3 } } \cs_generate_variant:Nn \object_newconst_tl:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_str:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_int:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_clist:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_dim:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_skip:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_fp:nnn { Vnn } \cs_generate_variant:Nn \object_newconst_str:nnn { nnx } \cs_generate_variant:Nn \object_newconst_str:nnn { nnV } % \end{macrocode} %\end{macro} % % \begin{macro}{\object_newconst_seq_from_clist:nnn} % Creates a |seq| constant. % \begin{macrocode} \cs_new_protected:Nn \object_newconst_seq_from_clist:nnn { \seq_const_from_clist:cn { \object_ncmember_adr:nnn { #1 }{ #2 }{ seq } } { #3 } } \cs_generate_variant:Nn \object_newconst_seq_from_clist:nnn { Vnn } % \end{macrocode} %\end{macro} % % \begin{macro}{\object_newconst_prop_from_keyval:nnn} % Creates a |prop| constant. % \begin{macrocode} \cs_new_protected:Nn \object_newconst_prop_from_keyval:nnn { \prop_const_from_keyval:cn { \object_ncmember_adr:nnn { #1 }{ #2 }{ prop } } { #3 } } \cs_generate_variant:Nn \object_newconst_prop_from_keyval:nnn { Vnn } % \end{macrocode} %\end{macro} % % % \begin{macro}{\object_ncmethod_adr:nnn, \object_rcmethod_adr:nnn} % Fully expands to the method address. % \begin{macrocode} \cs_new:Nn \object_ncmethod_adr:nnn { #1 \tl_to_str:n { _ CMETHOD _ #2 : #3 } } \cs_generate_variant:Nn \object_ncmethod_adr:nnn { Vnn , vnn } \cs_new:Nn \object_rcmethod_adr:nnn { \object_ncmethod_adr:vnn { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { P }{ str } } { #2 }{ #3 } } \cs_generate_variant:Nn \object_ncmethod_adr:nnn { Vnn , vnn } \cs_generate_variant:Nn \object_rcmethod_adr:nnn { Vnn } % \end{macrocode} % \end{macro} % % \begin{macro}[pTF]{\object_ncmethod_if_exist:nnn, \object_rcmethod_if_exist:nnn} % Tests if the specified member constant exists. % \begin{macrocode} \prg_new_conditional:Nnn \object_ncmethod_if_exist:nnn {p, T, F, TF } { \cs_if_exist:cTF { \object_ncmethod_adr:nnn { #1 }{ #2 }{ #3 } } { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \object_rcmethod_if_exist:nnn {p, T, F, TF } { \cs_if_exist:cTF { \object_rcmethodr_adr:nnn { #1 }{ #2 }{ #3 } } { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \object_ncmethod_if_exist:nnn { Vnn }{ p, T, F, TF } \prg_generate_conditional_variant:Nnn \object_rcmethod_if_exist:nnn { Vnn }{ p, T, F, TF } % \end{macrocode} % \end{macro} % % \begin{macro}{\object_new_cmethod:nnnn} % Creates a new method % \begin{macrocode} \cs_new_protected:Nn \object_new_cmethod:nnnn { \cs_new:cn { \object_ncmethod_adr:nnn { #1 }{ #2 }{ #3 } } { #4 } } \cs_generate_variant:Nn \object_new_cmethod:nnnn { Vnnn } % \end{macrocode} % \end{macro} % % \begin{macro}{\object_ncmethod_call:nnn, \object_rcmethod_call:nnn} % Calls the specified method. % \begin{macrocode} \cs_new:Nn \object_ncmethod_call:nnn { \use:c { \object_ncmethod_adr:nnn { #1 }{ #2 }{ #3 } } } \cs_new:Nn \object_rcmethod_call:nnn { \use:c { \object_rcmethod_adr:nnn { #1 }{ #2 }{ #3 } } } \cs_generate_variant:Nn \object_ncmethod_call:nnn { Vnn } \cs_generate_variant:Nn \object_rcmethod_call:nnn { Vnn } % \end{macrocode} % \end{macro} % % \begin{macrocode} \cs_new_protected:Nn \@@_initproxy:nnn { \object_newconst:nnnn { \object_embedded_adr:nn{ #3 }{ /_I_/ } } { ifprox }{ bool }{ \c_true_bool } } \cs_generate_variant:Nn \@@_initproxy:nnn { VnV } % \end{macrocode} % % %\begin{macro}[pTF]{\object_if_proxy:n} %Test if an object is a proxy. % \begin{macrocode} \cs_new:Nn \@@_bol_com:N { \cs_if_exist_p:N #1 && \bool_if_p:N #1 } \cs_generate_variant:Nn \@@_bol_com:N { c } \prg_new_conditional:Nnn \object_if_proxy:n {p, T, F, TF} { \cs_if_exist:cTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { ifprox }{ bool } } { \bool_if:cTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { ifprox }{ bool } } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } % \end{macrocode} %\end{macro} % %\begin{macro}[pTF]{\object_test_proxy:nn, \object_test_proxy:nN} %Test if an object is generated from selected proxy. % \begin{macrocode} \prg_generate_conditional_variant:Nnn \str_if_eq:nn { ve }{ TF } \prg_new_conditional:Nnn \object_test_proxy:nn {p, T, F, TF} { \str_if_eq:veTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { P }{ str } } { #2 } { \prg_return_true: } { \prg_return_false: } } \prg_new_conditional:Nnn \object_test_proxy:nN {p, T, F, TF} { \str_if_eq:cNTF { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { P }{ str } } #2 { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \object_test_proxy:nn { Vn }{p, T, F, TF} \prg_generate_conditional_variant:Nnn \object_test_proxy:nN { VN }{p, T, F, TF} % \end{macrocode} %\end{macro} % % \begin{macro}{\object_create:nnnNN, \object_create_set:NnnnNN, \object_create_gset:NnnnNN, \object_create:nnnN, \object_create_set:NnnnN, \object_create_gset:NnnnN, \object_create:nnn, \object_create_set:Nnnn, \object_create_gset:Nnnn, \embedded_create:nnn} % Creates an object from a proxy. % \begin{macrocode} \msg_new:nnnn { rawobjects }{ notproxy }{ Fake ~ proxy } { Object ~ #1 ~ is ~ not ~ a ~ proxy. } \cs_new_protected:Nn \@@_force_proxy:n { \object_if_proxy:nF { #1 } { \msg_error:nnn { rawobjects }{ notproxy }{ #1 } } } \cs_new_protected:Nn \@@_create_anon:nnnNN { \tl_if_empty:nF{ #1 } { \@@_force_proxy:n { #1 } \object_newconst_str:nnn { \object_embedded_adr:nn{ #3 }{ /_I_/ } } { M }{ #2 } \object_newconst_str:nnn { \object_embedded_adr:nn{ #3 }{ /_I_/ } } { P }{ #1 } \object_newconst_str:nnV { \object_embedded_adr:nn{ #3 }{ /_I_/ } } { S } #4 \object_newconst_str:nnV { \object_embedded_adr:nn{ #3 }{ /_I_/ } } { V } #5 \seq_map_inline:cn { \object_member_adr:nnn { #1 }{ varlist }{ seq } } { \object_new_member:nnv { #3 }{ ##1 } { \object_ncmember_adr:nnn { #1 }{ ##1 _ type }{ str } } } \seq_map_inline:cn { \object_member_adr:nnn { #1 }{ objlist }{ seq } } { \embedded_create:nvn { #3 } { \object_ncmember_adr:nnn { #1 }{ ##1 _ proxy }{ str } } { ##1 } } \tl_map_inline:cn { \object_member_adr:nnn { #1 }{ init }{ tl } } { ##1 { #1 }{ #2 }{ #3 } } } } \cs_generate_variant:Nn \@@_create_anon:nnnNN { xnxNN, xvxcc } \cs_new_protected:Nn \object_create:nnnNN { \@@_create_anon:xnxNN { #1 }{ #2 } { \object_address:nn { #2 }{ #3 } } #4 #5 } \cs_generate_variant:Nn \object_create:nnnNN { VnnNN } \cs_new_protected:Nn \object_create_set:NnnnNN { \object_create:nnnNN { #2 }{ #3 }{ #4 } #5 #6 \str_set:Nx #1 { \object_address:nn { #3 }{ #4 } } } \cs_new_protected:Nn \object_create_gset:NnnnNN { \object_create:nnnNN { #2 }{ #3 }{ #4 } #5 #6 \str_gset:Nx #1 { \object_address:nn { #3 }{ #4 } } } \cs_generate_variant:Nn \object_create_set:NnnnNN { NVnnNN, NnnfNN } \cs_generate_variant:Nn \object_create_gset:NnnnNN { NVnnNN, NnnfNN } \cs_new_protected:Nn \object_create:nnnN { \object_create:nnnNN { #1 }{ #2 }{ #3 } #4 \c_object_public_str } \cs_generate_variant:Nn \object_create:nnnN { VnnN } \cs_new_protected:Nn \object_create_set:NnnnN { \object_create_set:NnnnNN #1 { #2 }{ #3 }{ #4 } #5 \c_object_public_str } \cs_new_protected:Nn \object_create_gset:NnnnN { \object_create_gset:NnnnNN #1 { #2 }{ #3 }{ #4 } #5 \c_object_public_str } \cs_generate_variant:Nn \object_create_set:NnnnN { NVnnN } \cs_generate_variant:Nn \object_create_gset:NnnnN { NVnnN } \cs_new_protected:Nn \object_create:nnn { \object_create:nnnNN { #1 }{ #2 }{ #3 } \c_object_global_str \c_object_public_str } \cs_generate_variant:Nn \object_create:nnn { Vnn } \cs_new_protected:Nn \object_create_set:Nnnn { \object_create_set:NnnnNN #1 { #2 }{ #3 }{ #4 } \c_object_global_str \c_object_public_str } \cs_new_protected:Nn \object_create_gset:Nnnn { \object_create_gset:NnnnNN #1 { #2 }{ #3 }{ #4 } \c_object_global_str \c_object_public_str } \cs_generate_variant:Nn \object_create_set:Nnnn { NVnn } \cs_generate_variant:Nn \object_create_gset:Nnnn { NVnn } \cs_new_protected:Nn \embedded_create:nnn { \@@_create_anon:xvxcc { #2 } { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { M }{ str } } { \object_embedded_adr:nn { #1 }{ #3 } } { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { S }{ str } } { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { V }{ str } } } \cs_generate_variant:Nn \embedded_create:nnn { nvn, Vnn } % \end{macrocode} %\end{macro} % %\begin{macro}{\proxy_create:nn, \proxy_create_set:Nnn, \proxy_create_gset:Nnn} %Creates a new proxy object % \begin{macrocode} \cs_new_protected:Nn \proxy_create:nn { \object_create:VnnNN \c_proxy_address_str { #1 }{ #2 } \c_object_global_str \c_object_public_str } \cs_new_protected:Nn \proxy_create_set:Nnn { \object_create_set:NVnnNN #1 \c_proxy_address_str { #2 }{ #3 } \c_object_global_str \c_object_public_str } \cs_new_protected:Nn \proxy_create_gset:Nnn { \object_create_gset:NVnnNN #1 \c_proxy_address_str { #2 }{ #3 } \c_object_global_str \c_object_public_str } \cs_new_protected:Nn \proxy_create:nnN { \@@_launch_deprecate:NN \proxy_create:nnN \proxy_create:nn \object_create:VnnNN \c_proxy_address_str { #1 }{ #2 } \c_object_global_str #3 } \cs_new_protected:Nn \proxy_create_set:NnnN { \@@_launch_deprecate:NN \proxy_create_set:NnnN \proxy_create_set:Nnn \object_create_set:NVnnNN #1 \c_proxy_address_str { #2 }{ #3 } \c_object_global_str #4 } \cs_new_protected:Nn \proxy_create_gset:NnnN { \@@_launch_deprecate:NN \proxy_create_gset:NnnN \proxy_create_gset:Nnn \object_create_gset:NVnnNN #1 \c_proxy_address_str { #2 }{ #3 } \c_object_global_str #4 } % \end{macrocode} %\end{macro} % %\begin{macro}{\proxy_push_member:nnn} % Push a new member inside a proxy. % \begin{macrocode} \cs_new_protected:Nn \proxy_push_member:nnn { \object_newconst_str:nnn { #1 }{ #2 _ type }{ #3 } \seq_gput_left:cn { \object_member_adr:nnn { #1 }{ varlist }{ seq } } { #2 } } \cs_generate_variant:Nn \proxy_push_member:nnn { Vnn } % \end{macrocode} %\end{macro} % %\begin{macro}{\proxy_push_embedded:nnn} % Push a new embedded object inside a proxy. % \begin{macrocode} \cs_new_protected:Nn \proxy_push_embedded:nnn { \object_newconst_str:nnx { #1 }{ #2 _ proxy }{ #3 } \seq_gput_left:cn { \object_member_adr:nnn { #1 }{ objlist }{ seq } } { #2 } } \cs_generate_variant:Nn \proxy_push_embedded:nnn { Vnn } % \end{macrocode} %\end{macro} % %\begin{macro}{\proxy_add_initializer:nN} % Push a new embedded object inside a proxy. % \begin{macrocode} \cs_new_protected:Nn \proxy_add_initializer:nN { \tl_gput_right:cn { \object_member_adr:nnn { #1 }{ init }{ tl } } { #2 } } \cs_generate_variant:Nn \proxy_add_initializer:nN { VN } % \end{macrocode} %\end{macro} % %\begin{variable}{\c_proxy_address_str} % Variable containing the address of the \verb|proxy| object. % \begin{macrocode} \str_const:Nx \c_proxy_address_str { \object_address:nn { rawobjects }{ proxy } } \object_newconst_str:nnn { \object_embedded_adr:Vn \c_proxy_address_str { /_I_/ } } { M }{ rawobjects } \object_newconst_str:nnV { \object_embedded_adr:Vn \c_proxy_address_str { /_I_/ } } { P } \c_proxy_address_str \object_newconst_str:nnV { \object_embedded_adr:Vn \c_proxy_address_str { /_I_/ } } { S } \c_object_global_str \object_newconst_str:nnV { \object_embedded_adr:Vn \c_proxy_address_str { /_I_/ } } { V } \c_object_public_str \@@_initproxy:VnV \c_proxy_address_str { rawobjects } \c_proxy_address_str \object_new_member:Vnn \c_proxy_address_str { init }{ tl } \object_new_member:Vnn \c_proxy_address_str { varlist }{ seq } \object_new_member:Vnn \c_proxy_address_str { objlist }{ seq } \proxy_push_member:Vnn \c_proxy_address_str { init }{ tl } \proxy_push_member:Vnn \c_proxy_address_str { varlist }{ seq } \proxy_push_member:Vnn \c_proxy_address_str { objlist }{ seq } \proxy_add_initializer:VN \c_proxy_address_str \@@_initproxy:nnn % \end{macrocode} %\end{variable} % % \begin{macro}{\object_allocate_incr:NNnnNN, \object_gallocate_incr:NNnnNN, \object_allocate_gincr:NNnnNN, \object_gallocate_gincr:NNnnNN} % Create an address and use it to instantiate an object % \begin{macrocode} \cs_new:Nn \@@_combine_aux:nnn { anon . #3 . #2 . #1 } \cs_generate_variant:Nn \@@_combine_aux:nnn { Vnf } \cs_new:Nn \@@_combine:Nn { \@@_combine_aux:Vnf #1 { #2 } { \cs_to_str:N #1 } } \cs_new_protected:Nn \object_allocate_incr:NNnnNN { \object_create_set:NnnfNN #1 { #3 }{ #4 } { \@@_combine:Nn #2 { #3 } } #5 #6 \int_incr:N #2 } \cs_new_protected:Nn \object_gallocate_incr:NNnnNN { \object_create_gset:NnnfNN #1 { #3 }{ #4 } { \@@_combine:Nn #2 { #3 } } #5 #6 \int_incr:N #2 } \cs_generate_variant:Nn \object_allocate_incr:NNnnNN { NNVnNN } \cs_generate_variant:Nn \object_gallocate_incr:NNnnNN { NNVnNN } \cs_new_protected:Nn \object_allocate_gincr:NNnnNN { \object_create_set:NnnfNN #1 { #3 }{ #4 } { \@@_combine:Nn #2 { #3 } } #5 #6 \int_gincr:N #2 } \cs_new_protected:Nn \object_gallocate_gincr:NNnnNN { \object_create_gset:NnnfNN #1 { #3 }{ #4 } { \@@_combine:Nn #2 { #3 } } #5 #6 \int_gincr:N #2 } \cs_generate_variant:Nn \object_allocate_gincr:NNnnNN { NNVnNN } \cs_generate_variant:Nn \object_gallocate_gincr:NNnnNN { NNVnNN } % \end{macrocode} % \end{macro} % % %\begin{macro}{\object_assign:nn} %Copy an object to another one. % \begin{macrocode} \cs_new_protected:Nn \object_assign:nn { \seq_map_inline:cn { \object_member_adr:vnn { \object_ncmember_adr:nnn { \object_embedded_adr:nn{ #1 }{ /_I_/ } } { P }{ str } } { varlist }{ seq } } { \object_member_set_eq:nnc { #1 }{ ##1 } { \object_member_adr:nn{ #2 }{ ##1 } } } } \cs_generate_variant:Nn \object_assign:nn { nV, Vn, VV } % \end{macrocode} %\end{macro} % % \begin{macrocode} % % \end{macrocode} % %\end{implementation} % %\PrintIndex