Any way to bundle some variables in an OOP-like object in LaTeX?
PGF has an object-oriented engine, that supports - according to the manual
classes, methods, constructors, attributes, objects, object identities, inheritance and overloading.
Here's what it looks like:
\pgfooclass{stamp}{
% This is the class stamp
\method stamp() { % The constructor
}
\method apply(#1,#2) { % Causes the stamp to be shown at coordinate (#1,#2)
% Draw the stamp:
\node [rotate=20,font=\huge] at (#1,#2) {Passed};
}
}
\pgfoonew \mystamp=new stamp()
\begin{tikzpicture}
\mystamp.apply(1,2)
\mystamp.apply(3,4)
\end{tikzpicture}
While this is usually probably not the best way of doing things, you could just emulate the namespace of a struct by creating appropriate macro names:
\documentclass{article}
\usepackage{mwe} % <-- only for example image
\usepackage{xparse} % <-- only for \setfigurestruct
\makeatletter
\def\setstructfield#1#2#3{\expandafter\def\csname @struct@#1@field@#2\endcsname{#3}}
\def\getstructfield#1#2{\csname @struct@#1@field@#2\endcsname}
\def\ifstructhasfield#1#2{\ifcsname @struct@#1@field@#2\endcsname \expandafter\@firstoftwo\else \expandafter\@secondoftwo\fi}
\makeatother
% \setfigurestruct{name}{path}[width as fraction of \linewidth]{caption}[label]
\NewDocumentCommand\setfigurestruct{m O{.8} m m o}{%
\setstructfield{#1}{width}{#2\linewidth}%
\setstructfield{#1}{path}{#3}%
\setstructfield{#1}{caption}{#4}%
\IfValueT{#5}{%
\setstructfield{#1}{label}{#5}%
}%
}
% \plotfigure{figure struct name}
\newcommand*\plotfigure[1]{%
\begin{figure}%
\centering
\includegraphics[width=\getstructfield{#1}{width}]{\getstructfield{#1}{path}}%
\caption{\getstructfield{#1}{caption}}%
\ifstructhasfield{#1}{label}{\label{\getstructfield{#1}{label}}}{}%
\end{figure}%
}
\begin{document}
\setfigurestruct{testfig}{example-image-a}{A test image.}[fig:test]
Look at figure~\ref{fig:test}, please.
\plotfigure{testfig}
\end{document}
The fields are not really stored as a structure here, but the interface acts as if they were.
A more powerful alternative would be using pgfkeys
. You could write some wrapper functions emulating the "feel" of structs (similar to what I did above) if you want that, although I don't really see the sense in that.
A riskier alternative to schtandard's solution is to make delimited macros. This will*-ish* stick to the class.property
syntax. We just have to define a delimited macro that expects a dot after it:
\documentclass{article}
\usepackage{graphicx}
\makeatletter
\def\newobj#1{%
\ifcsname #1\endcsname
\errmessage{Cannot define #1. Control sequence already defined.}
\fi%
\expandafter\def\csname obj@#1\endcsname{}%
\expandafter\def\csname #1\endcsname.##1 {%
\csname obj@#1@##1\endcsname%
}%
}
\def\addtoobj#1#2#3{%
\ifcsname obj@#1\endcsname\else
\errmessage{Object #1 undefined. Use \string\newobj\{#1\}}
\fi%
\expandafter\edef\csname obj@#1@#2\endcsname{#3}%
}
\makeatother
\newcommand{\plotfigure}[1]{%
\begin{figure}
\centering
\edef\Fw{[width=}
\expandafter\expandafter\expandafter\includegraphics\expandafter\Fw#1.width ]{#1.url }
\caption{#1.caption }
\label{#1.label }
\end{figure}
}
\begin{document}
\pagenumbering{gobble}
\newobj{Figure}
\addtoobj{Figure}{url}{example-image}
\addtoobj{Figure}{caption}{This is a figure.}
\addtoobj{Figure}{width}{0.5\linewidth}
\addtoobj{Figure}{label}{a_nice_label}
\plotfigure{\Figure}
See figure \ref{\Figure.label }
\end{document}
With this approach you create a "class" with \newobj{<name>}
and add properties to it with \addtoobj{<name>}{<property>}{<value>}
.
When you use, for example, \newobj{Figure}
, a macro \Figure.#1␣
(␣
is a space) is created. This macro expands to \obj@Figure@#1
. Then, when you call \addtoobj{Figure}{url}{example-image}
, the macro \obj@Figure@url
is defined to be example-image
. Then, a call to \Figure.url␣
(notice the space!) will expand to example-image
.
Be careful, whenever you use this, the \Figure
macro must be followed by a .
, then by a (valid) property name, then by a space. The space is part of the command!
As for your \plotfigure
macro, we can use the \Figure.#1␣
without many problems, except for the optional argument to \includegraphics
. The optional argument has to be expanded before the call to \includegraphics
, so an \expandafter
trickery is needed here. Other than that, \plotfigure{\Figure}
works like a charm :)