It’s a bit of a bummer that, because list(FIND...)
returns 0
-based index values, and if()
interprets 0
and only 0
as a FALSE
value, the results of a list(FIND...)
have to be compared to -1
in order to be used as truth values.
Woe betide the CMakeLists.txt
author who assumes this will do what they think it does:
# NOTE: Example of BROKEN code, DO NOT USE
list(FIND AVAILABLE_THINGS ${something} _found_it)
if(_found_it)
message(STATUS "Found ${something}!")
endif()
If ${something}
is in the first list postition, they’ll get a 0
for _found_it
and the test will fail. If it’s actually not found, they’ll get a -1
for _found_it
and the test will succeed.
But sometimes, you really do just want to test for presence in a list. It’s a pattern that’s used extensively in the bundled scripts, in fact. Here’s an example from FindCURL.cmake
:
foreach(component IN LISTS CURL_FIND_COMPONENTS)
list(FIND CURL_KNOWN_PROTOCOLS ${component} _found)
if(NOT _found EQUAL -1)
list(FIND CURL_SUPPORTED_PROTOCOLS ${component} _found)
if(NOT _found EQUAL -1)
set(CURL_${component}_FOUND TRUE)
elseif(CURL_FIND_REQUIRED)
message(FATAL_ERROR "CURL: Required protocol ${component} is not found")
endif()
else()
list(FIND CURL_SUPPORTED_FEATURES ${component} _found)
if(NOT _found EQUAL -1)
set(CURL_${component}_FOUND TRUE)
elseif(CURL_FIND_REQUIRED)
message(FATAL_ERROR "CURL: Required feature ${component} is not found")
endif()
endif()
endforeach()
If CMake truly supported a boolean list-membership test, let’s call it list(CONTAINS)
, one possible rewrite for that loop woud be:
foreach(component IN LISTS CURL_FIND_COMPONENTS)
list(CONTAINS CURL_KNOWN_PROTOCOLS ${component} _known_proto)
if(_known_proto)
set(_type "protocol")
list(CONTAINS CURL_SUPPORTED_PROTOCOLS ${component} CURL_${component}_FOUND)
else()
set(_type "feature")
list(CONTAINS CURL_SUPPORTED_FEATURES ${component} CURL_${component}_FOUND)
endif()
if(CURL_FIND_REQUIRED AND NOT CURL_${component}_FOUND)
message(FATAL_ERROR "CURL: Required ${_type} ${component} is not found")
endif()
endforeach()
(You could also squirrel away the _type
string as CURL_${component}_TYPE
with 0 extra lines of code, in case it would be useful to have later.)
So my questions are:
- Has a
list(CONTAINS)
or similar been considered in the past? - If so, was the idea rejected? Why?
- Would patches1 to add such a command be entertained?
Notes
- (Patches which would be fantastically easy to write, effectively
HandleContainsCommand
would just be a copy-paste — as much as I tend to loathe copy-paste code — of the existingHandleFindCommand
fromcmListCommand.cxx
, butreturningstoring boolean values instead of list indices.)