# imakeidx \index expands macros; makeindex rejects everything

I have the following minimal(ish) example. This is for a technical book with lots of code samples and lots of code-keywords-appearing-in-running-text. So I have the following goals:

• A macro \code{foo} for typesetting code-keywords-in-running-text. I use the listings package for typesetting code throughout. I also use the underscore package so I don't have to escape underscores in code.
• I use the hyperref package for cross-references, plus plenty of code keywords appearing in section titles; so I use \texorpdfstring in the definition of \code.
• Two indices: a main "Index" for English words and concepts, and a second "Index of code samples". I have heard that the imakeidx package is the most standard choice for this, so I'm using it. I'm open to alternatives if they can fix my problem.
• Many, complicated, index terms. Some terms must be typeset with \code{foo}. Some must be secondary entries, as in, "iterator, std::vector::: see vector::iterator."

## mce.tex

\documentclass[ebook,10pt,oneside,final]{memoir}

\usepackage{imakeidx}
\makeindex[name=mce,intoc,columns=2]
\makeindex[name=code,intoc,columns=2,name=code,title=Index of code samples]

\usepackage[final]{listings}
\usepackage{hyperref}
\usepackage{underscore}

\newcommand{\code}[1]{\texorpdfstring{\mbox{\lstinline[basicstyle=\ttfamily]#1}}{#1}}

\newcommand{\codeblockdefines}[1]{\index[code]{#1@\code{#1}}}  % for example, \codeblockdefines{list_of_int}
\newcommand{\codeindex}[1]{\index{#1@\code{#1}}}  % for example, \codeindex{const_iterator}
\newcommand{\codeindexstd}[1]{\index{namespace std@\code{namespace std}!#1@\code{#1}}}  % for example, \codeindexstd{vector}

\begin{document}

\tableofcontents

Here's an example of using \code{std::vector}.
\codeindexstd{vector}
\codeindex{std::vector}
\codeblockdefines{vector}

\index{std@\code{std}|see {namespace std@\code{namespace std}}}

\printindex[mce]
\printindex[code]

\end{document}


The problem I'm seeing is that \code and maybe some other macros are getting expanded in the .idx files. When I cat these files I see this:

## mce.idx

\indexentry{namespace std@\unhbox \voidb@x \hbox {\lstinline [basicstyle=\ttfamily  ]namespace std}!vector@\unhbox \voidb@x \hbox {\lstinline [basicstyle=\ttfamily  ]vector}|hyperpage}{1}
\indexentry{std::vector@\unhbox \voidb@x \hbox {\lstinline [basicstyle=\ttfamily  ]std::vector}|hyperpage}{1}
\indexentry{std@\code{std}|hyperindexformat{\see {namespace std@\code{namespace std}}}}{1}


## code.idx

\indexentry{vector@\unhbox \voidb@x \hbox {\lstinline [basicstyle=\ttfamily  ]vector}|hyperpage}{1}


How can I make \index and the rest of my convenience macros (\codeblockdefines, \codeindex, \codeindexstd) do what I want?

• First move the call to imakeidx before loading hyperref. Oct 9, 2017 at 22:37
• @egreg: Okay, done. The output doesn't change, though. (EDIT: ok, the .idx files change a bit, but not significantly enough to not-be-rejected.) Oct 9, 2017 at 22:42
• I should add that I also see only one "Index" in my final .pdf file, but I think that's just a side-effect of having had all the entries in the "Index of code samples" be rejected. I think TeX refuses to create a page for an index if it'd have zero entries in it. Oct 9, 2017 at 22:50
• Your commands \codeindex and \codeindexstd point to a non existent index. Oct 9, 2017 at 22:53

imakeidx should be loaded before hyperref. However, your \code command is very fragile as it uses \lstinline and should be “robusted”.

\documentclass[ebook,10pt,oneside,final]{memoir}

\usepackage[final]{listings}
\usepackage{imakeidx}
\usepackage{underscore}
\usepackage{etoolbox}
\usepackage{hyperref}

\makeindex[name=mce,intoc,columns=2]
\makeindex[name=code,intoc,columns=2,name=code,title=Index of code samples]

\newrobustcmd{\code}[1]{\texorpdfstring{\mbox{\lstinline[basicstyle=\ttfamily]{#1}}}{#1}}

\newcommand{\codeblockdefines}[1]{\index[code]{#1@\code{#1}}}  % for example, \codeblockdefines{list_of_int}
\newcommand{\codeindex}[1]{\index[mce]{#1@\code{#1}}}  % for example, \codeindex{const_iterator}
\newcommand{\codeindexstd}[1]{\index[mce]{namespace std@\code{namespace std}!#1@\code{#1}}}  % for example, \codeindexstd{vector}

\begin{document}

\tableofcontents

Here's an example of using \code{std::vector}.
\codeindexstd{vector}
\codeindex{std::vector}
\codeblockdefines{vector}

\index{std@\code{std}|see {namespace std@\code{namespace std}}}

\printindex[mce]
\printindex[code]

\end{document}


I changed the \codeindex and \codeindexstd commands to point to a defined index.

The code.idx file will contain

\indexentry{vector@\code {vector}|hyperpage}{1}


whereas mce.idx contains

\indexentry{namespace std@\code {namespace std}!vector@\code {vector}|hyperpage}{1}
\indexentry{std::vector@\code {std::vector}|hyperpage}{1}


These entries are not rejected.

• Re "to point to a defined index" — My understanding was that "mce" would be the name of the main index for "mce.tex", so I didn't need to specify that name explicitly; i.e. I could write \index{foo} instead of \index[mce]{foo}. Is this wrong and/or too clever? Should I just define a convenience macro \newcommand{\idx}[1]{\index[mce]{#1}} and avoid using raw \index? Oct 9, 2017 at 22:52
• @Quuxplusone No, the unadorned \index command only refers to one not passed the name option. Just remove name=mce, if you want to use \index{...} for pointing to it. Oct 9, 2017 at 22:54
• I'll look into the etoolbox package. Does it require the change you made to use curly braces instead of backticks? My code samples use a lot of curly braces... Oct 9, 2017 at 22:55
• @Quuxplusone You can use backquotes, but they do nothing more than braces, because the argument is already tokenized. \newrobustcmd is just more efficient than \DeclareRobustCommand. Oct 9, 2017 at 22:56
• Awesome, it seems to work! (Although robustifying the whole \code macro broke \texorpdfstring, so what I did was I pulled out \newrobustcmd{\codeinternal}[1]{\mbox{\lstinline[basicstyle=\ttfamily]#1}} and then \newcommand{\code}[1]{\texorpdfstring{\codeinternal{#1}}{#1}}.) I'm still getting "Improper alphabetic constant" for the index entry \indexentry{list_of_ints!::iterator@\code {list_of_ints!::iterator}}{13}, and none of my "X, see Y" entries are showing up; but I'm sure those are unrelated issues and I'll ask new questions if I can't figure them out. Oct 9, 2017 at 23:19