2

I'm writing a template which uses the report class and customizes the typesetting with several packages. I'm defining some data in a separate user-data.tex file which is loaded just after documentclass:

\def\thesis{Master} %<PhD> or <Master>
\def\thesistitle{The title of the thesis}
\def\author{AName AFirst ASecond}
\def\authormail{aname@univ.edu}
\def\school{Master and Doctoral School}
\def\date{City, month year}
\def\logo-university{univ.pdf}

Those variables/constants are used to provide two different titlepages, to be included in the footer with titlesec, or used by the user at any time.

Is it ok to do it like that (using just \def)? Or should I use \global, \newcommand... or any other ? Would you place them in any other format rather than plain tex?


After the comments, I changed to \newcommand* and renamed some of the macros:

\newcommand*\thesis{Master} %<PhD> or <Master>
\newcommand*\thesistitle{The title of the thesis}
\newcommand*\authors{AName AFirst ASecond}
\newcommand*\authorsmail{aname@univ.edu}
\newcommand*\school{Master and Doctoral School}
\newcommand*\titledate{City, month year}
\newcommand*\university{univ.pdf}

After the answer by @Andrew, I'm using

\providecommand*\@school{No SCHOOL defined}
\newcommand*\School[1]{\renewcommand*\@school{#1}}

just in the beginning of the package, to let the user either define \newcommand*\@school{Something} before loading it, or \School{Something} after doing so.


As @barbara guessed, I moved the template to a *.sty which I'm loading as a package:

mypkg.sty

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypkg}

%If not previously defined, default value applied
\providecommand*{\@mythesis}{No THESIS TYPE defined}
\providecommand*{\@myauthor}{No AUTHOR defined}
%Command to modify the field at any point after this
\newcommand*\MyThesis[1]{\renewcommand*\@mythesis{#1}}
\newcommand*\MyAuthor[1]{\renewcommand*\@myauthor{#1}}
%Command to access the field
\newcommand*\showDF[1]{\csname @#1\endcsname}

%The content may be supplied in a separate file with \Thesis
\@input{data.dat}

\endinput

main.tex

\documentclass[a4paper,titlepage,11pt,twoside,openright]{report}

% A field may defined before loading the package
\makeatletter
\newcommand\@mythesis{My title before loading}
\makeatother
%If it not defined, the package does it
\usepackage{mypkg}

\begin{document}

% The variable may be directly accessed
\makeatletter\@mythesis\makeatother
% The content may be rewritten
\MyThesis{PhD}
% Reading with the provided command
\showDF{mythesis}
\showDF{myauthor}

\end{document}

It works. The user can define the content prior to loading the package, or either in a separate file or at any point after doing so with a 'helper command'. However, I'd like to:

-Use a command to automatically define the commands associated to a field. New question here.

-Give the user the ability to give the field as an option when loading the package. I tried with xkeyval:

\define@key{fam}{thesis}[Master]{\Thesis{#1}}
\ProcessOptionsX<fam>

changing the loading in the main file to:

\usepackage[thesis=PhD]{mypkg}

The above code works, except for the fields which have more than a word, since the spaces between them are removed. How can I make it work with, i.e. \usepackage[title=A title with several words]{mypkg}. I tried with {A title with several words} and "A title with several words" but It doesn't work.

It can not be made


Using the approach in this answer, I have now this 'factory' function to create all the commands at once:

\usepackage{etoolbox}
\newcommand\forcsvlistargs[2]{ \expandafter\providecommand\csname \detokenize{#1}aux\endcsname[1]{
 \csname \detokenize{#1}\endcsname##1\relax}
 \forcsvlist{\csname \detokenize{#1}aux\endcsname}{#2}
}
\newcommand\newDF[2]{ \expandafter\providecommand\csname th#1\endcsname{No \MakeUppercase{#2} defined}
 \expandafter\newcommand\csname TH#2\endcsname[1]{\expandafter\renewcommand\csname th#1\endcsname{##1}} }

\forcsvlistargs{newDF}{{{typ}{type}}, {{date}{date}}, {{tit}{title}}, {{sch}{school}}, 
{{aut}{author}}, {{eaut}{eauthor}} }
  • 1
    fairly trivial comment -- hyphens aren't permitted in command names, so \logo-university would be better with a different name. – barbara beeton Sep 23 '14 at 20:34
  • 1
    one problem with using \def is that you don't get warned about overwriting existing commands eg \author and \date – David Carlisle Sep 23 '14 at 21:10
  • Another thing to consider is if you want to allow a user to define these as \newcommands before your file is loaded. If so, I'd use \providecommand. Otherwise they have to use \renewcommand after the file is loaded. – Peter Grill Sep 24 '14 at 5:50
  • @barbarabeeton \catcode-=11\def\logo-university\includegraphics{logo}` ;-) oh the markup fails, but you get the point – 1010011010 Sep 24 '14 at 20:02
  • 1
    @1010011010 -- yes, you can do that, but if you have other hyphens in the file, things are likely to go awry in ways you didn't foresee. – barbara beeton Sep 24 '14 at 20:06
3

I have some (internal) classes that do something like this. What I do is first define a command like:

\newcommand\School[1]{\def\@school{#1}}

so that the "user" can use \School{My wonderful school} to overwrite the defaults. Internally, the class uses \@school whenever it needs to print the name of the school.

Next, if this is reasonable I have the class set a reasonable default for the school by having a line like

\School{My Wonderful School}

in the class. In fact, it is probably better form to use

\providescommand\@school{My Wonderful School}

although using the "helper command" is clearer, I think.

One place where I use this is for tutorial sheets where it happens that many of these variables depend on something else. For these cases I define an option to the class for setting all of these variables in one hit. So the "user" starts their file with

\documentclass[somecourse,solutions]{mytutorials}

and then inside mytutorials.cls I have

\DeclareOption{somecourse}{
  \CourseName{An exciting counrse}
  \Semester{Semester 2}
  \CourseNumber{Math 987123}
}
\DeclareOption{solutions}{
    ...
}

To process all of the options you need something like:

\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions

This allows me to piggyback off article.cls and pass it options that are not defined in my file.

Finally, depending on your use-case, you might want to automatically load a set of defaults from a file in the current directory. If you use \include or \input then you will run into problems when the file does not exist. Instead you can use \@input inside your class file:

\@input{defaults.dat}

This will load the file "defaults.dat" if it exists and, otherwise, do nothing. This file will be included as is, so there is a danger in using this trick because if the file contains garbage everything breaks. Of course, to cover the cases when this file does not exist, you need to set reasonable defaults.

(Btw, for your own internal variables there is really no harm in using \def -- and it is easier too. Of course, you should first make sure that you are not overwriting anything else!)

  • Thanks a lot for you answer. As you can see, I edited the question and took your approach into account. However, when trying to pass the fields as options of the package (not a class in my case, although I suppose there's no much difference) the spaces between words disappear. In the example you provided you use single-word keys. Would you mind having a look at the edited question? – U.Martinez-Corral Sep 25 '14 at 0:27
  • @U.Martinez-Corral There seem to be three variations for using keys: xkeyval, kvoptions and pgfopts. None of them support spaces in the keys, so I think that you are out of luck. The way that I cope with this is that I have a single keyword (without spaces) that then sets multiword parameter (with spaces). – Andrew Sep 25 '14 at 0:58
  • I was afraid of hearing that... but have to cope with it :D. Aniway, it is already much 'easier' than it was when I did the question! On the other hand, have you ever tried with \csname to define several commands at once? – U.Martinez-Corral Sep 25 '14 at 1:05
  • I should have added that I have not ever used keys, so even though I did not find a way of allowing spaces perhaps there is one. For your second question, instead of \csname...\endcsname you can also use the LaTeX primitives \@namedefand \@nameuse - these both just wrap the corresponding TeX commands. The etoolbox package provides many variations like \csdef, \csuse, ... These commands are both easier to read and easier to use. – Andrew Sep 25 '14 at 1:12
  • I'm trying with \providecommand\@nameuse{@#1}{No #2 defined} and \providecommand\csuse{@#1}{No #2 defined}, but none of them work. \expandafter\providecommand\csname @#1\endcsname{No {\MakeUppercase #2} defined} works, but the \MakeUppercase has no effect. I would prefer not to use @namedef and \csdef since I'd like to check if those commands have been previously set. – U.Martinez-Corral Sep 25 '14 at 2:05

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.