How to check if a column type is defined?
The instruction
\newcolumntype{N}{...}
defines the command \NC@find@N
. You can check it with
\ifcsname NC@find@\string#1\endcsname
A \safenewcolumntype
command can be defined by
\makeatletter
\newcommand\@gobbleoptandone[2][]{}
\expandafter\let\csname safenct@l\endcsname\@empty
\expandafter\let\csname safenct@c\endcsname\@empty
\expandafter\let\csname safenct@r\endcsname\@empty
\expandafter\let\csname safenct@p\endcsname\@empty
\expandafter\let\csname safenct@m\endcsname\@empty
\expandafter\let\csname safenct@b\endcsname\@empty
\expandafter\let\csname safenct@@\endcsname\@empty
\expandafter\let\csname safenct@!\endcsname\@empty
\expandafter\let\csname safenct@|\endcsname\@empty
\expandafter\let\csname safenct@<\endcsname\@empty
\expandafter\let\csname safenct@>\endcsname\@empty
\expandafter\let\csname safenct@=\endcsname\@empty
\def\safenct@error#1{\PackageError{mypackage}
{Column type `\string#1' already defined}
{The column type you're trying to define is already\MessageBreak
existent; I'll ignore this new definition}}
\newcommand{\safenewcolumntype}[1]{%
\@tempswafalse
\ifcsname NC@find@\string#1\endcsname
\safenct@error{#1}\@tempswatrue
\fi
\ifcsname safenct@#1\endcsname
\safenct@error{#1}\@tempswatrue
\fi
\if@tempswa
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi{\@gobbleoptandone}{\newcolumntype{#1}}%
}
\makeatother
If the column type is already defined we put before the rest of the token list the command \@gobbleoptandone
that will gobble the optional argument and the mandatory one: \safenewcolumntype{l}[1]{...}
would become
\@gobbleoptandone[1]{...}
and all is good.
Otherwise we put \newcolumntype{#1}
that will perform its duty. Of course it would be better to patch the original \newcolumntype
command.
Patching the original definition
With the code between \makeatletter
and \makeatother
, we redefine the \newcolumntype
command in such a way that it checks whether the column type is already defined or not and, in the former case, it disallows redefining the column type. One could define also a \renewcolumntype
command, but it doesn't seem this important.
\documentclass{article}
\usepackage{array}
\makeatletter
\newcommand\@gobbleoptandone[2][]{}
\def\newcolumntype#1{%
\@tempswafalse
\edef\NC@char{\string#1}%
\@ifundefined{NC@find@\NC@char}%
{\@tfor\next:=<>clrmbp@!|\do
{\if\next\NC@char
\PackageError{safenct}{`\NC@char' is a primitive column type}
{You're trying to redefine a primitive column type;\MessageBreak
the redefinition will be ignored}%
\global\@safenct@deftrue
\fi}%
\if@tempswa\else\NC@list\expandafter{\the\NC@list\NC@do#1}\fi}%
{\PackageError{safenct}{Column `\NC@char' is already defined}
{This column type is already defined;\MessageBreak
the redefinition will be ignored}%
\@tempswatrue}%
\if@tempswa
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\@gobbleoptandone}
{\@namedef{NC@find@\NC@char}##1#1{\NC@{##1}}%
\@ifnextchar[{\newcol@{\NC@char}}{\newcol@{\NC@char}[0]}}
}
\makeatother
\newcolumntype{N}{>{Y}l<{Z}}
\newcolumntype{N}{r}
\newcolumntype{l}{r}
\begin{document}
\begin{tabular}{N}
xxxx\\
x\\
xx
\end{tabular}
\end{document}
When \newcolumntype{x}
is scanned, the routine sets the scratch conditional to false and checks if x
is among the primitive column types and, if it is, issues an error message and sets the temporary conditional to true. Then it checks whether x
is among the "derived" column types, that is, defined via \newcolumntype
itself. If it is, the scratch conditional is again set to false. Finally, as in the code presented before, the proper action is taken: if the conditional is false the following part of the code for \newcolumntype
is gobbled, otherwise the definition is performed.
Although this does not directly answer your question, the array
package provides \showcols
which (according to the array
documentation) does the following:
A list of all the currently active
\newcolumntype
definitions is sent to the terminal and.log
file.
For example, if your .tex
file contains:
\newcolumntype{X}{l}
\newcolumntype{Y}{@{}c<{\hspace{1ex}}@{\ }}
\showcols
your .log
file will contain
Column X -> l
Column Y -> @{}c<{\hspace {1ex}}@{\ }