Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Column types for tables can be defined and overwritten with \newcolumntype. I would like to know if a column type is already defined for an example which shall only be executed in the case where the columntype is defined.

EDIT: Similar to the answer of egreg I had also created this code:

\newcommand\isColumntypeDefined[1]{%
  \providebool{\tpl@coltype@#1}
  \ifcsdef{NC@find@\string#1}{
    \setboolean{\tpl@coltype@#1}{true}%
  }{%
    \setboolean{\tpl@coltype@#1}{false}
  }%
  \boolean{\tpl@coltype@#1}%
}

but that fails because \tpl@coltype@#1 does not create a valid command. This problem is related to latex @temps(wa) commands. It would be great to know how to make this code compileable.

EDIT2: I changed the whole code to etoolbox definitions and finally ended with this

% Create list of predefined columntypes
\expandafter\let\csname columntype@l\endcsname\@empty
\expandafter\let\csname columntype@c\endcsname\@empty
\expandafter\let\csname columntype@r\endcsname\@empty
\expandafter\let\csname columntype@p\endcsname\@empty
\expandafter\let\csname columntype@m\endcsname\@empty
\expandafter\let\csname columntype@b\endcsname\@empty

\newcommand\CheckIfColumntypeDefined[1]{%
  \providebool{tpl@coltype@#1}
  \ifcsdef{NC@find@\string#1}%
    {\setbool{tpl@coltype@#1}{true}}%
    {\ifcsdef{columntype@\string#1}
      {\setbool{tpl@coltype@#1}{true}}%
      {\setbool{tpl@coltype@#1}{false}}%
    }%
}

\newcommand\isColumntypeDefined[1]{tpl@coltype@#1}

\newcommand\IfColumntypeDefined[3]{%
  \CheckIfColumntypeDefined{#1}
  \ifboolexpr{ bool{\isColumntypeDefined{#1}} }{#2}{#3}%
}
share|improve this question
add comment

2 Answers

up vote 7 down vote accepted

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.

share|improve this answer
    
I had also found this definition in array.sty. Is it also valid without array.sty? Also see my edit above. –  Matthias Pospiech Nov 20 '11 at 21:30
    
@MatthiasPospiech No, it isn't. Actually the "primitive" column types pass the test! The task is a bit more complicated. –  egreg Nov 20 '11 at 21:52
add comment

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}}@{\ }
share|improve this answer
    
I know, but I want to use this test within the document code. –  Matthias Pospiech Nov 21 '11 at 6:34
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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