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
HandleContainsCommandwould just be a copy-paste — as much as I tend to loathe copy-paste code — of the existingHandleFindCommandfromcmListCommand.cxx, butreturningstoring boolean values instead of list indices.)