Functions And Macros

Simplest Of Functions

function(simple)
  message("that was simple")
endfunction()

simple()
$ cmake -P function-simple.cmake
that was simple

Function Scope

  • Variables are local to a function

    function(simple)
      set(some_variable "value")
    endfunction()
    
    simple()
    
    if (NOT DEFINED some_variable)
      message("pooh, not seeing function side effect")
    endif()
    
    $ cmake -P function-scope.cmake
    pooh, not seeing function side effect
    
  • Caller’s (parent’s) variables are visible inside a function

    function(access_parent)
      message("inside function: ${some_variable}")
    endfunction()
    
    set(some_variable "outside value")
    access_parent()
    
    $ cmake -P function-scope-parent.cmake
    inside function: outside value
    
  • Function cannot modify caller’s variables ⟶ function scope is a copy of caller’s scope

Function Scope: And Nested Function?

  • Calling functions inherit their scope into called functions

    function(inner_function)
      message("inner_function: \${outer_variable}: ${outer_variable}")
    endfunction()
    
    function(outer_function)
      set(outer_variable "outer value")
      inner_function()
    endfunction()
    
    outer_function()
    
    $ cmake -P function-nested-scope.cmake
    inner_function: ${outer_variable}: outer value
    

Passing Parameters In

  • In function “body”, formal parameters (e.g. param) are treated just like variables

    function(take_one_parameter param)
      message("${param}")
    endfunction()  
    
    take_one_parameter("the parameter")
    
    $ cmake -P function-one-parameter.cmake
    the parameter
    
  • Superfluous parameters are discarded ⟶ quoting!

    function(take_one_parameter param)
      message("${param}")
    endfunction()  
    
    take_one_parameter(the parameter)
    
    $ cmake -P function-one-parameter-passed-two.cmake
    the
    
  • … whereas missing parameters cause a CMake error

    function(take_one_parameter param)
      message("${param}")
    endfunction()  
    
    take_one_parameter()
    
    $ cmake -P function-one-parameter-passed-none.cmake
    CMake Error at function-one-parameter-passed-none.cmake:5 (take_one_parameter):
      take_one_parameter Function invoked with incorrect arguments for function
      named: take_one_parameter
    
  • This is a function that takes two formal parameters

    function(take_two_parameters param1 param2)
      message("param1: ${param1}, param2: ${param2}")
    endfunction()  
    
    take_two_parameters("the first parameter" "the second parameter")
    
    $ cmake -P function-two-parameters.cmake
    param1: the first parameter, param2: the second parameter
    

Parameters: ARGC, ARGV, ARGVn, ARGN

  • ARGC: number of arguments passed

    function(argv_etc first_param second_param)
      message("${ARGC}")
    endfunction()
    
    argv_etc("one" "two")
    argv_etc("one" "two" "three")
    
    $ cmake -P function-argc.cmake
    2
    3
    
  • ARGV: list of all arguments

    function(argv_etc first_param second_param)
      message("${ARGV}")
    endfunction()
    
    argv_etc("one" "two")
    argv_etc("one" "two" "three")
    
    $ cmake -P function-argv.cmake
    one;two
    one;two;three
    
  • ARGVn: referencing arguments by position (invalid position ⟶ empty/undefined)

    function(argv_etc first_param second_param)
      message("ARGV0: >${ARGV0}<")
      message("ARGV1: >${ARGV1}<")
      message("ARGV3000: >${ARGV3000}<")
    endfunction()
    
    argv_etc("one" "two")
    
    $ cmake -P function-argvn.cmake
    ARGV0: >one<
    ARGV1: >two<
    ARGV3000: ><
    
  • ARGN: arguments past the last formal argument

    function(argv_etc first_param second_param)
      message("${ARGN}")
    endfunction()
    
    argv_etc("one" "two" "three" "four")
    
    $ cmake -P function-argn.cmake
    three;four
    

And Output Variables? Return Values?

  • There is no return statement

  • ⟶ functions aren’t expressions

  • No “output” parameters (like references in C++, or pointers in C)

  • … but there is set(... PARENT_SCOPE)

PARENT_SCOPE Examples

  • Creating a variable in caller scope just because we can

    function(reach_out)
      set(caller_variable "ha, I modified parent scope" PARENT_SCOPE)
    endfunction()  
    
    reach_out()
    
    message("caller_variable magically appeared: >${caller_variable}<")
    
    $ cmake -P function-reaching-out.cmake
    caller_variable magically appeared: >ha, I modified parent scope<
    
  • Modifying a variable passed in by the caller (“output variable”, “pass by reference”)

    function(reach_out ref_param)
      set(${ref_param} "callee's value" PARENT_SCOPE)
    endfunction()  
    
    set(some_variable "caller's value")
    message("some_variable before call: >${some_variable}<")
    reach_out(some_variable)
    message("some_variable after call: >${some_variable}<")
    
    $ cmake -P function-output-parameter.cmake
    some_variable before call: >caller's value<
    some_variable after call: >callee's value<
    
  • Popular trick, used by many CMake functions: set multiple variables with the same prefix in caller scope

    function(reach_out stem)
      set(${stem}_one "one value" PARENT_SCOPE)
      set(${stem}_another "another value" PARENT_SCOPE)
    endfunction()  
    
    reach_out(some_variable)
    
    message(${some_variable_one})
    message(${some_variable_another})
    
    $ cmake -P function-reaching-out-stem.cmake
    one value
    another value
    

Passing Any Number Of Parameters

  • Special parameters ARGC (number of arguments) and ARGV (of list type)

    function(takes_any_number_args)
      message("number of args passed: ${ARGC}")
      message("args passed: ${ARGV}")
    endfunction()
    
    takes_any_number_args("one" "two")
    
    $ cmake -P function-varargs.cmake
    number of args passed: 2
    args passed: one;two
    
  • Accessing parameters by position

    function(takes_any_number_args)
      message("${ARGV0}")
      message("${ARGV1}")
    endfunction()
    
    takes_any_number_args("one" "two")
    
    $ cmake -P function-varargs-by-position.cmake
    one
    two
    

Fancy Parameter Parsing: cmake_parse_arguments()

function(takes_fancy_parameters)
  cmake_parse_arguments(MYPARAMS
    "VERBOSE;COOL"         # options/flags
    "FANCY1;FANCY2"        # single-value parameters
    "FANCIES1;FANCIES2"    # multi-value parameters

    ${ARGV}
  )

  if (MYPARAMS_VERBOSE)
    message("being verbose")
  endif()
  if (MYPARAMS_COOL)
    message("being cool")
  endif()

  if (DEFINED MYPARAMS_FANCY1)
    message("FANCY1: >${MYPARAMS_FANCY1}<")
  else()
    message("FANCY1: undefined")
  endif()

  if (DEFINED MYPARAMS_FANCY2)
    message("FANCY2: >${MYPARAMS_FANCY2}<")
  else()
    message("FANCY2: undefined")
  endif()

  if (DEFINED MYPARAMS_FANCIES1)
    message("FANCIES1: >${MYPARAMS_FANCIES1}<")
  else()
    message("FANCIES1: undefined")
  endif()

  if (DEFINED MYPARAMS_FANCIES2)
    message("FANCIES2: >${MYPARAMS_FANCIES2}<")
  else()
    message("FANCIES2: undefined")
  endif()
endfunction()

takes_fancy_parameters(FANCIES1 "one-fancy" "two-fancy" VERBOSE FANCY2 "fancy")
$ cmake -P function-parse-args.cmake
being verbose
FANCY1: undefined
FANCY2: >fancy<
FANCIES1: >one-fancy;two-fancy<
FANCIES2: undefined

And Macros?

  • Just like functions

  • … only without own scope

  • ⟶ share caller’s scope

macro(simple)
  set(some_variable "value")            # <--- no PARENT_SCOPE
endmacro()

simple()

message("some_variable: >${some_variable}<")
$ cmake -P macro-simple.cmake
some_variable: >value<