1

I am trying to create a document template that would be used by many people where the end users can control certain values but most of the template style can be controlled centrally. Imagine a (simplified) directory structure that looks something like this:

.
├── latex
│   ├── front_pages.tex
│   ├── packages.tex
│   └── preamble.tex
├── Makefile
├── src
│   └── user_config.tex
├── sty
│   └── department_thesis.sty
└── user_thesis.tex

The idea is to have the template have distinct end user editable files (user_config.tex) and the rest be the centrally controlled "template" files (the whole thing is going to be used as a Git submodule) that the end user should never have to touch. If I want the end user to be able to control certain things though inputs in user_config.tex is there a "best" or most "LaTeX-ic" way of doing this by creating end user accessible variables with \newcommand?

For example, if I want to define a command in department_thesis.sty that the end user will then use as a "variable" in user_config.tex that will then actually get used in front_pages.tex what are people's suggestions on how to best do this?

Would it be something like

# department_thesis.sty
\newcommand\AuthorFirstName[1]{\newcommand{\authorfirstname}{#1}}

.

# user_config.tex
\AuthorFirstName{Graduate} % End user puts in their name here

.

# front_pages.tex
...
\authorfirstname % Here it is actually being used
...

Or is it better style to think in the mindset of setters/getters?

# department_thesis.sty
\newcommand\SetAuthorFirstName[1]{\newcommand{\AuthorFirstName}{#1}}

.

# user_config.tex
\SetAuthorFirstName{Graduate} % End user puts in their name here

.

# front_pages.tex
...
\AuthorFirstName % Here it is actually being used
...

Or is none of this a good way of thinking about the problem of wanting an end user to have control over certain values while being able to control the rest of the document's style centrally?

  • 2
    In general I prefer key-value syntax for such things. So the user get a command \templatesetup{firstname=Matthew, lastname= Feickert,...} Such a setup is easy to use and easy to extend. – Ulrike Fischer Jun 8 '18 at 20:12
1

I made the following for my handin-package. The code in the answer is different as according to the edits:

Edit 1: \@macroname@noerror now will now not give an error when default value is not set.

Edit 2: Code behaves identically, but added comments and removed some repetitions for compactness. Also added an example usecase of the generated \ifset@ macro.

I suspect that printing the command name to the page might be unwanted behaviour. To remove this just remove the line that contains the comment %<- Print command name to page.. Furthermore: read the comments. Everything is explained in some detail :)

\documentclass{article}
\begin{document}
  %%% Usage:
  %%%\settable{text}
  %%% The text you enter would be a macro:
  %%%: \settable{hello}
  %%%:  if now \@hello is called,
  %%%:  a warning is displayed with
  %%%:  the text "\hello not set"
  %%%: \hello{world}
  %%%: if now \@hello is called, it prints "world"
  %%%: \@hello@noerror gives the returning
  %%%: content and empty without error if no content set.
  \makeatletter
  \let\ea = \expandafter
  \newcommand{\settable}[2][\@nil]{
    %%% example call \settable[actionIfNotSet(default action)]{macroName}
    %%% i.e. #1: is what to do if called and not set and
    %%% #2 is the macroname
    %
    %------ Example case ------%
    % The comments assume that someone called
    % \settable[world]{hello}
    %------ Code ------%
    %
    % First define the \hello command. The code below
    % is equivalent to
    % \def\hello##1{
    %   \def\@hello{##1}
    %   \def\@hello@noerror{##1}
    % }
    \ea\def\csname #2\ea\endcsname##1{
      \ea\def\csname @#2\endcsname{##1}
      \ea\def\csname @#2@noerror\endcsname{##1}
      \ea\def\csname isset@#2\endcsname{1}%<- Used by \ifset@hello. Explained below:
    }
    % Now \hello is a callable macro.
    %
    % Next: create the command to check if \hello is defined.
    % usage: \ifset@hello{User has set the macro}{User has not set the macro}
    % Below is equivalent to
    % \def\ifset@hello##1##2{
    % \ifcsname isset@hello\endcsname% <- if the macro \isset@hello exists. \isset@hello is defined when \hello is called
    %   ##1%
    % \else
    %   ##2%
    % \fi
    % }
    \ea\def\csname ifset@#2\endcsname##1##2{
    \ifcsname isset@#2\endcsname%
      ##1%
    \else
      ##2%
    \fi
    }
    % Now, make a macro to hold the default value and
    % define it to be the given optional argument (\@nil by default, "world" in the
    % example). The code below is equivalent (in the example case)
    % to \def\default@hello{world}
    \ea\def\csname default@#2\endcsname{#1}%
    % Check if optional argument is given (e.g. if it is equal to \@nil)
    \ifx#1\@nil\relax
      % If no default value given, then set e.g. \@hello@noerror to be an empty value.
      % The very first version of this answer would not have the below line.
      \ea\def\csname @#2@noerror\endcsname{}%
      % Also, if the opt arg is not given we define the default behaviour,
      % \default@hello in the example case
      % to print a console warning, as well as printing the macro name to the page
      \ea\def\csname default@#2\endcsname{
        {\@latex@warning{\@backslashchar #2 not given}}% <- Console warning
        \textbackslash #2% <- Print command name to page.
      }
    \else
      % If default value given, then set the \@hello@noerror to that value.
      % Since "world" was given as optional in the example case, this will be
      % the same as \def\@hello@noerror{world}
      \ea\def\csname @#2@noerror\endcsname{#1}%
    \fi

    % We now define the usage macro \@hello:
    \ea\def\csname @#2\endcsname{
      \csname default@#2\endcsname
    }
  }
  \settable{hello}
  % A summary of generated macros
  \ifset@hello{     %<-  Prints "User did not set hello"
    User did set hello
  }{
    User did not set hello
  }
  \@hello@noerror   %<- Empty value. Does nothing extra by default.
  \@hello           %<- Gives warning message "\hello not given", and prints "\hello" on page.
  \hello{world}     %<- Sets \@hello and \@hello@noerror to "world"
  \ifset@hello{     %<- Prints "User did set hello"
    User did set hello
  }{
    User did not set hello
  }
  \@hello           %<- Prints "world"
  \@hello@noerror   %<- Prints "world"
\end{document}
|improve this answer|||||
  • This is extremely nice and does essentially exactly what I wanted. Thank you. Do I have your permission to use the above code snippet in this thesis template project (licence BSD-3-Clause) if I properly attribute it to you and link to your handin package? – Matthew Feickert Jun 8 '18 at 20:27
  • Sure! Go ahead! I'd love if you'd link to it somewhere, but no pressure :) – Andreas Storvik Strauman Jun 8 '18 at 20:31
  • It's originally distributed with the LPPL-1.3c-Licence :) – Andreas Storvik Strauman Jun 8 '18 at 20:37
  • Ah, so if I try to use the noerror behavior in the example above I get an Undefined control sequence. Any thoughts on why (I'm using TeX Live 2017 and LuaLaTeX)? ! Undefined control sequence. \@hello@noerror ->\@nil – Matthew Feickert Jun 9 '18 at 20:32
  • @MatthewFeickert That happens if the default value is \@nil. You can either remove the \@nil from the default optional argument or use \settable[]{commandname}. No error is intended as no warning shown to the user, heh. Misnamed I guess :) – Andreas Storvik Strauman Jun 10 '18 at 10:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.