2

I am trying to create an environment allows for displaying two environments side by side. These two environments are \code environments. A \code environment allows for a title for the code to be passed as well as the code itself.

The solution I have come up with is using a minipage around the first environment and another minipage around the second environment. Now, instead of doing all of this work over and over again, I would like to define a new environment that allows for two titles to be passed as well as the codes. Perhaps something like this: \compare{title 1}{title 2}{code 1}{code 2}

How can this be achieved? Thank you in advance.

Output

\documentclass{article}
\usepackage{enumitem}
\usepackage{listings}
\usepackage[most]{tcolorbox}

\makeatletter
\newenvironment{code}[1][]{%
    \@totalleftmargin=0pt%
    \def\foot{#1} 
    \tcbwritetemp}%
    {\endtcbwritetemp%
    \ifx\foot\@empty
    \tcbox[before = \begin{center}, after = \end{center},
    fonttitle = \color{white}\itshape\ttfamily, colframe = blue, 
    listing only, listing options = {language = c++}
    ]%
    {\tcbusetemplisting}%
    \else
    \tcbox[title = {\foot}, % The title option is present here
    before = \begin{center}, after = \end{center},
    fonttitle = \color{white}\itshape\ttfamily, colframe = blue, 
    listing only, listing options = {language = c++}
    ]%
    {\tcbusetemplisting}%
    \fi
}%
\makeatother

\lstset{
    frameround=fttt,
    language=c++,
    breaklines=true,
    keywordstyle=\color{blue}\bfseries, 
    basicstyle=\ttfamily\color{red},
    numberstyle=\color{black},
    keepspaces=true
    }

% DOESN'T WORK
\newenvironment{compare}[4]{
\begin{minipage}{0.4\textwidth}
\begin{code}[#1]
#3
\begin{code}
\end{minipage}
\begin{minipage}{0.4\textwidth}
    \begin{code}[#2]
#4
    \begin{code}
\end{minipage}
}

\begin{document}    
    Start a list:
    \begin{enumerate}[label = \textbf{--}]
        \item The first item:\\
        \begin{minipage}[t]{0.4\textwidth}
            \begin{code}[title1]
xx
            \end{code}
        \end{minipage}
        \begin{minipage}[t]{0.4\textwidth}
            \begin{code}[title2]
yy
            \end{code}
        \end{minipage}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Instead of doing all the above, I want to have something like the following:
    %\compare[title 1][title 2]{int x = 0;}{int y = 1;}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \end{enumerate}
\end{document}
2

There are mismatching \begin{code} and \end{code} in your “DOESN'T WORK“ compare environment and \newenvironment is missing an argument (if you wanted to use \newcommand, you should have used \compare instead ofcompare). But the biggest problem is that verbatim material can't be conveniently captured in arguments, as far as I know; environments are better for that.

Then comes a second hurdle: you want to capture two such things at the same time, using one environment—but an environment has only one contents... The solution I propose is to use two environments listingA and listingB inside the compare environment, that are only valid there. These two environments store the verbatim text (code) in two different files which the compare environment can then read in order to display the code side by side, as you wish.

Notes:

  • For the equal height group option of tcolorbox to do its full effect, two compilations are needed in general. This option ensures that side-by-side listingA and listingB (i.e., from the same compare environment call) have the same height.

  • If you want to be able to start your listings with a # character, apparently ou have to give up on the fact that the argument to listingA and listingB (the listing title) is optional, see below.

\documentclass{article}
\usepackage{enumitem}
\usepackage[most]{tcolorbox}

\makeatletter

\lstset{
    frameround=fttt,
    language=c++,
    breaklines=true,
    keywordstyle=\color{blue}\bfseries,
    basicstyle=\ttfamily\color{red},
    numberstyle=\color{black},
    keepspaces=true
    }

\newtoks\titleListingA
\newtoks\titleListingB
% We need to define a new 'equal height group' every time the 'compare'
% environment is used, so that listings A and B in a given call of the
% 'compare' environment have the same height; but this height is not
% necessarily the same across several calls to the 'compare' environment. So,
% we'll use a name based on the number of times the 'compare' environment has
% been called in the document.
\newcounter{myCompareEnvCounter}

\newenvironment{listingA}[1][]{%
  \errmessage{Use of environment 'listingA' is invalid outside 'compare'}}{}

\newenvironment{listingB}[1][]{%
  \errmessage{Use of environment 'listingB' is invalid outside 'compare'}}{}

\newenvironment{compare}{%
  \renewenvironment{listingA}[1][]{%
    \global\titleListingA={##1}%
    \tcbverbatimwrite{\jobname_listingA.cxx}%
  }{\endtcbverbatimwrite\ignorespacesafterend}%
  %
  \renewenvironment{listingB}[1][]{%
    \global\titleListingB={##1}%
    \tcbverbatimwrite{\jobname_listingB.cxx}%
  }{\endtcbverbatimwrite\ignorespacesafterend}%
  \stepcounter{myCompareEnvCounter}%
  \ignorespaces
}{%
  \unskip
  % Common options for the two boxes: expand the counter representation
  \edef\mytcbOptions{%
    \unexpanded{width=(\linewidth-4pt)/2,
      before=, after=\hfill,
      fonttitle = \color{white}\itshape\ttfamily, colframe = blue,
      listing only, listing options = {language = c++},
      equal height group=myCompareGroup-}\themyCompareEnvCounter}%
  \expandafter\tcbset\expandafter{\mytcbOptions}%
  \par\noindent
  %
  \expandafter\ifx\expandafter\@nil\the\titleListingA\@nil
    \tcbset{notitle}%
  \else
    \tcbset{adjusted title={\the\titleListingA}}%
  \fi
  \tcbinputlisting{listing file={\jobname_listingA.cxx}}%
  %
  \expandafter\ifx\expandafter\@nil\the\titleListingB\@nil
    \tcbset{notitle}%
  \else
    \tcbset{adjusted title={\the\titleListingB}}%
  \fi
  \tcbinputlisting{listing file={\jobname_listingB.cxx}}%
  %
  \renewenvironment{listingA}[1][]{%
    \errmessage{Use of environment 'listingA' is invalid outside 'compare'}}{}%
  %
  \renewenvironment{listingB}[1][]{%
    \errmessage{Use of environment 'listingB' is invalid outside 'compare'}}{}%
  \ignorespacesafterend
}

\makeatother

\begin{document}
Start a list:
\begin{enumerate}[label = \textbf{--}]
\item The first item:
  \begin{compare}
    \begin{listingA}[\#verycool]
int a = 0;
    \end{listingA}
%
    \begin{listingB}[Second listing]
int b = 0;
    \end{listingB}
  \end{compare}

\item Other item:
  \begin{compare}
    \begin{listingA}            % no title here
// Comment here
#include <cstdlib>

int main(int argc, char **argv)
{
  return EXIT_SUCCESS;
}
    \end{listingA}
%
    \begin{listingB}[Some title]
int zzz = 12;
    \end{listingB}
  \end{compare}
\end{enumerate}
\end{document}

Screenshot

Here is the same code, but with the argument to listingA and listingB, i.e., the listing title, made mandatory (just use an empty or blank value if you want no title). This allows code listings to start with a # character, so is probably desirable. This version uses etoolbox's \ifblank command to test whether the argument in question is blank (i.e., is empty or only contains space tokens).

\documentclass{article}
\usepackage{enumitem}
\usepackage{etoolbox}
\usepackage[most]{tcolorbox}

\makeatletter

\lstset{
    frameround=fttt,
    language=c++,
    breaklines=true,
    keywordstyle=\color{blue}\bfseries,
    basicstyle=\ttfamily\color{red},
    numberstyle=\color{black},
    keepspaces=true
    }

\newtoks\titleListingA
\newtoks\titleListingB
% We need to define a new 'equal height group' every time the 'compare'
% environment is used, so that listings A and B in a given call of the
% 'compare' environment have the same height; but this height is not
% necessarily the same across several calls to the 'compare' environment. So,
% we'll use a name based on the number of times the 'compare' environment has
% been called in the document.
\newcounter{myCompareEnvCounter}

\newenvironment{listingA}[1][]{%
  \errmessage{Use of environment 'listingA' is invalid outside 'compare'}}{}

\newenvironment{listingB}[1][]{%
  \errmessage{Use of environment 'listingB' is invalid outside 'compare'}}{}

\newenvironment{compare}{%
  \renewenvironment{listingA}[1]{%
    \global\titleListingA={##1}%
    \tcbverbatimwrite{\jobname_listingA.cxx}%
  }{\endtcbverbatimwrite\ignorespacesafterend}%
  %
  \renewenvironment{listingB}[1]{%
    \global\titleListingB={##1}%
    \tcbverbatimwrite{\jobname_listingB.cxx}%
  }{\endtcbverbatimwrite\ignorespacesafterend}%
  \stepcounter{myCompareEnvCounter}%
  \ignorespaces
}{%
  \unskip
  % Common options for the two boxes: expand the counter representation
  \edef\mytcbOptions{%
    \unexpanded{width=(\linewidth-4pt)/2,
      before=, after=\hfill,
      fonttitle = \color{white}\itshape\ttfamily, colframe = blue,
      listing only, listing options = {language = c++},
      equal height group=myCompareGroup-}\themyCompareEnvCounter}%
  \expandafter\tcbset\expandafter{\mytcbOptions}%
  \par\noindent
  %
  \expandafter\ifblank\expandafter{\the\titleListingA}{%
    \tcbset{notitle}%
  }{%
    \tcbset{adjusted title={\the\titleListingA}}%
  }%
  \tcbinputlisting{listing file={\jobname_listingA.cxx}}%
  %
  \expandafter\ifblank\expandafter{\the\titleListingB}{%
    \tcbset{notitle}%
  }{%
    \tcbset{adjusted title={\the\titleListingB}}%
  }%
  \tcbinputlisting{listing file={\jobname_listingB.cxx}}%
  %
  \renewenvironment{listingA}[1][]{%
    \errmessage{Use of environment 'listingA' is invalid outside 'compare'}}{}%
  %
  \renewenvironment{listingB}[1][]{%
    \errmessage{Use of environment 'listingB' is invalid outside 'compare'}}{}%
  \ignorespacesafterend
}

\makeatother

\begin{document}
Start a list:
\begin{enumerate}[label = \textbf{--}]
\item The first item:
  \begin{compare}
    \begin{listingA}{\#verycool}
int a = 0;
    \end{listingA}
%
    \begin{listingB}{Second listing}
int b = 0;
    \end{listingB}
  \end{compare}

\item Other item:
  \begin{compare}
    \begin{listingA}{}          % no title here
#include <cstdlib>

int main(int argc, char **argv)
{
  return EXIT_SUCCESS;
}
    \end{listingA}
%
    \begin{listingB}{Some title}
int zzz = 12;
    \end{listingB}
  \end{compare}
\end{enumerate}
\end{document}

Screenshot of second version

| improve this answer | |

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.