4
votes

I'm trying to document methods in the same file as the generic. I want the usage section to contain the method, but I do not want an alias generated for the method. This is because I have many methods for the generic and I'd like to keep the index relatively clean.

I've tried both @rdname and @describeIn but both seem to automatically generate an \alias tag which then shows up in the index. I can get the desired result by manually editing the Rd file and removing the \alias{} entry, but that isn't really sustainable.

UPDATE: Just noticed the following from R CMD Check:

Functions with \usage entries need to have the appropriate \alias entries, and all their arguments documented.

So maybe what I'm looking for is not even legal.

2
All of a sudden all the method index entries vanished. I have no idea what I did different. - BrodieG
Nevermind, they are back after re-install. - BrodieG
S3 or S4? It matters. - hadley
Then there's no way around it as far as I know. Every documented function must be listed in the aliases, and every alias is listed in the index. - hadley
But maybe you're thinking about this the wrong way - the point of the index is to be comprehensive. I don't think it makes sense to worry about cluttering it up. - hadley

2 Answers

4
votes

You can use a multi-line @useage like so:

#' a generic called foo
#' 
#' @param x the only named parameter
#' 
#' @usage 
#' # you can call `foo()` this way
#' foo(x, ..., [n, ybar,])
#' # or  this way
#' foo(x, ..., na.rm = FALSE, details = FALSE)
#' # or even  this way
#' foo(x, ..., [n, ybar,] na.rm = FALSE, details = FALSE)

foo  <-  function(x,...)
    return('hello world')

which produces the following foo.Rd file:

% Generated by roxygen2 (4.1.0): do not edit by hand
% Please edit documentation in R/peb-utils.r
\name{foo}
\alias{foo}
\title{a generic called foo}
\usage{
# you can call `foo()` this way
foo(x, ..., [n, ybar,])
# or  this way
foo(x, ..., na.rm = FALSE, details = FALSE)
# or even  this way
foo(x, ..., [n, ybar,] na.rm = FALSE, details = FALSE)
}
\arguments{
\item{x}{the only named parameter}
}
\description{
a generic called foo
}

Unfortunately, this does raise some warnings in the R CMD check:

* checking for code/documentation mismatches ... WARNING
Codoc mismatches from documentation object 'foo':
foo
  Code: function(x, ...)
  Docs: function(x, ..., na.rm = FALSE, details = FALSE)
  Argument names in docs not in code:
    na.rm details

* checking Rd \usage sections ... WARNING

Undocumented arguments in documentation object 'foo'
  '...' 'na.rm' 'details'

Bad \usage lines found in documentation object 'foo':
  foo(x, ..., [n, ybar,])
  foo(x, ..., [n, ybar,] na.rm = FALSE, details = FALSE)

Functions with \usage entries need to have the appropriate \alias
entries, and all their arguments documented.
The \usage entries must correspond to syntactically valid R code.
See the chapter 'Writing R documentation files' in the 'Writing R
3
votes

Here's a way to use roxygen 5.0.0+ to export generic function methods without creating aliases at the same time, so that the methods aren't listed in the index but are still documented properly in the help page of the generic function. The advantages over the method proposed by @Jthorpe are two-fold:

  1. You don't have to manually spell out the calling signature for methods (after all, you've already done so by defining the method in the first place).

  2. The techniques employed are generally useful for manipulating Rd-file structure with roxygen, beyond the facility provided by @-tags.

First, export your generic/methods in the usual way. Notice that there is no @rdname, so aliases won't be created.

#' @export
my_generic <- function(x, ...) UseMethod("my_generic")

#' @export
my_generic.default <- function(x, a = NULL, ...) "Default method"

#' @export
my_generic.numeric <- function(x, a = 0, ...) "Numeric method"

Next, follow that with a roxygen block for my_generic. The noteworthy features of this block are: 1) an alias for the generic function will be created (by @name), but not for any of its methods; 2) the @evalRd tag (available since roxygen 5.0.0) evaluates its code to create the \usage part of the Rd file, programatically.

#' My generic function
#'
#' @evalRd rd_s3_usage("my_generic", "default", "numeric")
#' @param x Some object.
#' @param a Some object.
#' @param ... Stuff.
#' @name my_generic
NULL

The function rd_s3_usage() creates the required \usage block as an (escaped) string, in the proper format for documenting S3 methods.

cat(rd_s3_usage("my_generic", "default", "numeric"))

#> \usage{
#> my_generic(x, \dots)
#> 
#> \method{my_generic}{default}(x, a = NULL, \dots)
#> 
#> \method{my_generic}{numeric}(x, a = 0, \dots)
#> }

In creating rd_s3_usage(), I've written helper functions that are more general than the task at hand requires, for these can then be reused (or adapted) in other situations where one wants to generate Rd blocks programmatically.

rd_dots <- function(x) gsub("\\.\\.\\.", "\\\\dots", x)

# Figure out calling signature of a function (given by name)
call_sig <- function(nm, cmd = nm, ...) {
  f <- get(nm, mode = "function", ...)
  sig <- deparse(call("function", formals(f), quote(expr = )))
  sig <- paste(trimws(sig, which = "left"), collapse = "")
  sig <- sub("^function", cmd, trimws(sig, which = "both"))

  rd_dots(sig)
}

# Make a vector of \usage{} entries for an S3 generic
s3_methods <- function(generic, ...) {
  classes <- list(...)
  rd_tmpl <- sprintf("\\\\method{%s}{%%s}", generic)
  cs_methods <- vapply(classes, function(cls) {
    method <- paste(generic, cls, sep = ".")
    rd_cmd <- sprintf(rd_tmpl, cls)
    call_sig(method, rd_cmd)
  }, character(1))

  c(call_sig(generic), cs_methods)
}

# Rd command markup
rd_markup <- function(cmd, join, sep) {
  force(join); force(sep)
  rd_cmd_opening <- paste0("\\", cmd, "{")

  function(x)
    paste(rd_cmd_opening, paste(x, collapse = join), "}", sep = sep)
}

rd_s3_usage <- function(...)
  rd_markup("usage", join = "\n\n", sep = "\n")(s3_methods(...))

Alas, running R CMD check still produces the dreaded Objects in \usage without \alias in documentation object 'my_generic' error. It seems that one must set method aliases to avoid it.