Associate counters -- stepping (many) counters simultaneously

Just for completeness, here's an implementation using expl3:

\documentclass{book}
\usepackage{xparse}

\usepackage{blindtext}
\usepackage{xcolor}
\usepackage{totcount}

\ExplSyntaxOn

\NewDocumentCommand{\DeclareAssociatedCounters}{mm}
 {
  \hupfer_declareassociatedcounters:nn { #1 } { #2 }
 }
\NewDocumentCommand{\AddAssociatedCounters}{mm}
 {
  \hupfer_addassociatedcounters:nn { #1 } { #2 }
 }
\NewDocumentCommand{\RemoveAssociatedCounters}{mm}
 {
  \hupfer_removeassociatedcounters:nn { #1 } { #2 }
 }
\NewDocumentCommand{\ClearAssociatedCountersList}{m}
 {
  \hupfer_declareassociatedcounters:nn { #1 } { }
 }

\cs_new_protected:Nn \hupfer_declareassociatedcounters:nn
 {
  \clist_clear_new:c { g_hupfer_bound_counters_#1_clist }
  \hupfer_addassociatedcounters:nn { #1 } { #2 }
 }

\cs_new_protected:Nn \hupfer_addassociatedcounters:nn
 {
  \clist_gput_right:cn { g_hupfer_bound_counters_#1_clist } { #2 }
  % or a slower routine if we want to check the items are counter names
  %\clist_map_inline:nn { #2 }
  % {
  %  \cs_if_exist:cTF { c@##1 }
  %   {
  %    \clist_gput_right:cn { g_hupfer_bound_counters_#1_clist } { ##1 }
  %   }
  %   {
  %    \ERROR{NOT A COUNTER}
  %   }
  % }
  %%%%
  \clist_gremove_duplicates:c { g_hupfer_bound_counters_#1_clist }
  % remove accidental #1 from the list
  \clist_gremove_all:cn { g_hupfer_bound_counters_#1_clist } { #1 }
 }

\cs_new_protected:Nn \hupfer_removeassociatedcounters:nn
 {
  \clist_map_inline:nn { #2 }
   {
    \clist_gremove_all:cn { g_hupfer_bound_counters_#1_clist } { ##1 }
   }
 }

\cs_new_protected:Npn \hupfer_stepcounter:n #1
 {
  \hupfer_orig_stepcounter:n { #1 }
  \clist_if_exist:cT { g_hupfer_bound_counters_#1_clist }
   {
    \clist_map_inline:cn { g_hupfer_bound_counters_#1_clist }
     {
      \hupfer_orig_stepcounter:n { ##1 }
     }
   }
 }

\AtBeginDocument
 {
  % save a copy of \stepcounter
  \cs_set_eq:NN \hupfer_orig_stepcounter:n \stepcounter
  % use the new one
  \cs_set_eq:NN \stepcounter \hupfer_stepcounter:n
 }

\ExplSyntaxOff

\newcommand{\ShowNiceCounterOutput}[5]{%
  \begin{center}
  \begin{tabular}{llll}
  & & & \\
  & \multicolumn{3}{c}{totcount page values} \\
  Page & \textcolor{red}{#1} & \textcolor{blue}{#2} & \textcolor{gray}{#3} \\
  \thepage & \textcolor{red}{\number\totvalue{#1}} &
    \textcolor{blue}{\number\totvalue{#2}} & \textcolor{gray}{\number\totvalue{#3}} \\
  & & & \\
  & \multicolumn{3}{c}{totcount section values} \\
  Section & \textcolor{red}{#4} & \textcolor{blue}{#5} \\
  \thesection & \textcolor{red}{\number\totvalue{#4}} &
    \textcolor{blue}{\number\totvalue{#5}} & \\
  & & & \\
  \end{tabular}
  \end{center}%
}

\ExplSyntaxOn
\NewDocumentCommand{\QuickOutput}{m}
 {
  \prg_replicate:nn { #1 }
   {
    \pagenumbering{arabic}%  -> pagenumber reset to zero , on purpose inside the loop
    \ShowNiceCounterOutput{page}{totalpages}{anotherpagescounter}{section}{totalsections}
    \blindtext
    \newpage%  some pages
   }
}
\ExplSyntaxOff

\begin{document}
\regtotcounter{page} % Register a total value counter --> this will be the driver counter
\newtotcounter{totalpages}% 1st driven counter
\newtotcounter{anotherpagescounter}% 2nd driven counter 
\newtotcounter{yetanotherpagescounter}% 3rd driven counter 


\regtotcounter{section}
\newtotcounter{totalsections}

\DeclareAssociatedCounters{page}{totalpages,anotherpagescounter}%  Register the driver and the driven counters
\DeclareAssociatedCounters{section}{section,totalsections}%  Register the driver and the driven counters for sections%
\AddAssociatedCounters{page}{yetanotherpagescounter}%  Register the driver and the driven counters



\pagenumbering{Roman}

\chapter{The first chapter}

\section{My first section}

% Generate dummy output
\QuickOutput{10}

\section{My second section}

\QuickOutput{10}

\pagenumbering{arabic}%  -> pagenumber reset to zero 

\appendix

\chapter{First Appendix chapter}

\section{My (only) appendix section}


% Generate dummy output, again ;-)
\QuickOutput{10}

% Clear the list of counters associated to page
\ClearAssociatedCountersList{page}

% Not necessary, only for removing some counters from list
\RemoveAssociatedCounters{page}{totalpages,anotherpagescounter}
% Readd the counter totalpages to the list --> in this context,
% It acts, as if it has not been removed at all
\DeclareAssociatedCounters{page}{totalpages}

\QuickOutput{20}

\end{document}

enter image description here


Update 2017/03/05 The assoccnt package has been superseded by xassoccnt and is available as version 1.3 on CTAN -- this package is much more powerful than assoccnt -- please don't use assoccnt any longer and switch to xassoccnt.

As egreg stated: the LaTeX kernel does not provide such a feature and perhaps there is other package doing this feature, so I have decided to provide a small package, being uploaded to CTAN and perhaps available soon.

The assoccnt.sty file

\NeedsTeXFormat{LaTeX2e}%
\ProvidesPackage{assoccnt}[2014/08/13 v0.2 -- Associate counters stepping]%
%%%
%% License: LaTeX Project Public License
%% Copyright (2014) Dr. Christian Hupfer 
%% Author: Christian Hupfer [email protected]
%%
%%
%%%%
%%% Some options later on
%%%%

\RequirePackage{etoolbox}[2011/01/03 2.2]%



\let\@@assoccnt@standardstepcounter\stepcounter%

\let\@@assoccnt@standardrefstepcounter\refstepcounter%


% Just for a quick suffix
\newcommand{\@@associatedcounterslistsuffix}{%
AssociatedCountersList%
}%



%%% Internal macro to generate the name of the list of associated counters
\newcommand{\@@assoccnt@@generatelistname}[1]{%
% #1 Name of the driver counter
@#1\@@associatedcounterslistsuffix
}%


\newcommand{\@@assoccnt@@addassociatedcounter}[2]{%
  \ifcsdef{#1}{%
    \ifinlistcs{#2}{#1}{%
      % Do nothing, since counter is already in the list
    }{%
      \listcsadd{#1}{#2}%
    }%
  }{%
    % Nothing in here
  }%
}%




% This command defines a list of counters, that should be stepped also if the driver counter
% is `\stepcounter`ed. 
% A self - association is not possible, as this would lead to inconsistent counting
\newcommand{\DeclareAssociatedCounters}[2]{%
% #1 --> driver counter 
% #2 --> CSV list of other counters, that should be stepped, if the driver counter is stepped
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%
    %  % Nothing to be done --> List already exists
    \GenericWarning{}{Warning: List of associated counters for counter #1 already exists}%
  }{%
    \csgdef{\@@assoccnt@@generatelistname{#1}}{}%   Define some global list 
  }%
  % Now add the counter names from #2 to the list 
  % Note: Currently, it is not checked whether a counter is already added!
  \forcsvlist{\@@assoccnt@@addassociatedcounter{\@@assoccnt@@generatelistname{#1}}}{#2}%
  % Now remove an accidental self-association
  \RemoveAssociatedCounter{#1}{#1}% 
}%



%%% A generic macro, that removes a list entry from the list by
%%% defining a temporary list and omitting the 
\newcommand{\@@assoccnt@removefromlist}[2]{%
% #1 list name
% #2 element to be removed
  \ifcsdef{#1}{%
    \gdef\@@mytemplist{}%
    \renewcommand*{\do}[1]{%
      \ifstrequal{##1}{#2}{
        % Later one some success routine etc. ???
      }{%
        \listgadd{\@@mytemplist}{##1}%
      }%
    }%
    \dolistcsloop{#1}%
    \csxdef{#1}{\@@mytemplist}%
  }{%
    % The list is not defined at all, can't remove something from something not existing...
  }%
}%



%% Remove a particular counter from the list 
\newcommand{\RemoveAssociatedCounter}[2]{%
% #1 arg: driver counter
% #2 arg: counter name to be removed
  \@@assoccnt@removefromlist{\@@assoccnt@@generatelistname{#1}}{#2}%
}%

%% Remove a CSV list of counters from the list of associated counters 

\newcommand{\RemoveAssociatedCounters}[2]{%
% #1 arg: driver counter
% #2 arg: CSV list of counters to be removed
  \forcsvlist{\RemoveAssociatedCounter{#1}}{#2}%
}%

%% Remove all associated counters from the list 
\newcommand{\ClearAssociatedCountersList}[1]{%
% #1 arg: driver counter
  \csundef{\@@assoccnt@@generatelistname{#1}}%
}%


%% Test if a counter is an associated counter of driver counter 
\newcommand{\IfIsAssociatedCounter}[4]{%
% #1 arg: driver counter
% #2 arg: (possibly) associated counter
% #3 arg: Code for execution on true branch
% #4 arg: Code for execution on false branch
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%
    \ifinlistcs{#2}{\@@assoccnt@@generatelistname{#1}}{%
      #3%
    }{%
      #4%
    }%
  }{% List does not exist, so it's not associated
    #4%
  }%
}%


%%% The stepcounter wrapper for the standard stepcounter command
%%% This is just for convenience, if the command as to be improved/extended later on in future
\newcommand{\@@assoccnt@stepcounter}[1]{%  
  \@@assoccnt@standardstepcounter{#1}%
}%

%%% Not needed so far
\newcommand{\@@assoccnt@refstepcounter}[1]{%  
  \@@assoccnt@standardrefstepcounter{#1}%
}%

%%%% Redefinition of the \stepcounter command 
\renewcommand{\stepcounter}[1]{%
  \@@assoccnt@standardstepcounter{#1}%   Traditional behaviour, since this is expected!
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%    Check first, whether the list exists at all, 
    \forlistcsloop{\@@assoccnt@stepcounter}{\@@assoccnt@@generatelistname{#1}}%  March through the list
  }{% 
    %No list -> do nothing at all
  }%
}%



%%%% Redefinition of the \stepcounter command 
\renewcommand{\refstepcounter}[1]{%
  \@@assoccnt@standardstepcounter{#1}%   Traditional behaviour, since this is expected!
  \protected@edef\@currentlabel%
  {\csname p@#1\endcsname\csname the#1\endcsname}%
  \ifcsdef{\@@assoccnt@@generatelistname{#1}}{%    Check first, whether the list exists at all, 
    \forlistcsloop{\@@assoccnt@stepcounter}{\@@assoccnt@@generatelistname{#1}}%  March through the list
  }{% 
    %No list -> do nothing at all
  }%
}%


\endinput%

And some test driver file

\documentclass{book}%

\usepackage{totcount}%
\usepackage{assoccnt}%

% Some packages only for output and dummy pages
\usepackage{blindtext}%
\usepackage{forloop}%  
\usepackage{xcolor}%

\newcounter{loopcounter}%

\newcommand{\ShowNiceCounterOutput}[5]{%
\begin{center}%
\begin{tabular}{llll}%
& & & \tabularnewline 
& \multicolumn{3}{c}{totcount page values} \tabularnewline
Page & \textcolor{red}{#1} & \textcolor{blue}{#2} & \textcolor{gray}{#3} \tabularnewline
\thepage & \textcolor{red}{\number\totvalue{#1}} & \textcolor{blue}{\number\totvalue{#2}} & \textcolor{gray}{\number\totvalue{#3}} \tabularnewline
& & & \tabularnewline 
 & \multicolumn{3}{c}{totcount section values} \tabularnewline 
Section & \textcolor{red}{#4} & \textcolor{blue}{#5} \tabularnewline
\thesection & \textcolor{red}{\number\totvalue{#4}} & \textcolor{blue}{\number\totvalue{#5}} & \tabularnewline
&  & & \tabularnewline 
\end{tabular}
\end{center}%
}%


\newcommand{\QuickOutput}[1]{%
%
\forloop{loopcounter}{1}{\value{loopcounter} < \numexpr #1+1}{%
\pagenumbering{arabic}%  -> pagenumber reset to zero , on purpose inside the loop
\ShowNiceCounterOutput{page}{totalpages}{anotherpagescounter}{section}{totalsections}%
\blindtext%
\newpage%  some pages
}%
}%



\begin{document}
\regtotcounter{page} % Register a total value counter --> this will be the driver counter
\newtotcounter{totalpages}% 1st driven counter
\newtotcounter{anotherpagescounter}% 2nd driven counter 
\newtotcounter{yetanotherpagescounter}% 3rd driven counter 


\regtotcounter{section}%
\newtotcounter{totalsections}

\DeclareAssociatedCounters{page}{totalpages,anotherpagescounter}%  Register the driver and the driven counters
\DeclareAssociatedCounters{section}{section,totalsections}%  Register the driver and the driven counters for sections%
\DeclareAssociatedCounters{page}{yetanotherpagescounter}%  Register the driver and the driven counters



\pagenumbering{Roman}% 

\chapter{The first chapter}%

\section{My first section}%

% Generate dummy output
\QuickOutput{10}%


\section{My second section}%

\QuickOutput{10}%

\pagenumbering{arabic}%  -> pagenumber reset to zero 

\appendix

\chapter{First Appendix chapter}%

\section{My (only) appendix section}%


% Generate dummy output, again ;-)
\QuickOutput{10}%

% Clear the list of counters associated to page
\ClearAssociatedCountersList{page}%

% Not necessary, only for removing some counters from list
\RemoveAssociatedCounters{page}{totalpages,anotherpagescounter}%  
% Readd the counter totalpages to the list --> in this context,
% It acts, as if it has not been removed at all
\DeclareAssociatedCounters{page}{totalpages}

\QuickOutput{20}%



\end{document}

The output of the driver is basically the same as in the snapshot of the question.

Notes

  • There is no test so far whether the arguments of the several commands are counter (names) at all
  • There is no test that two (or more) associated counters are not mutally associated counters.

I will provide updates and improvements continously.