[11/36] libcamera: yaml_parser: Replace getList() with get() specializations
diff mbox series

Message ID 20260113000808.15395-12-laurent.pinchart@ideasonboard.com
State New
Headers show
Series
  • libcamera: Global configuration file improvements
Related show

Commit Message

Laurent Pinchart Jan. 13, 2026, 12:07 a.m. UTC
The YamlObject class has two member function templates to get values:
the get() function gets a scalar value, while the getList() function
gets a vector of scalar values.

As get() is a function template, we can provide specializations for
vector types. This makes the code more systematic, and therefore more
readable. Replace all getList() occurrences, and drop the getList()
function.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/libcamera/internal/yaml_parser.h      |  19 ----
 src/ipa/libipa/agc_mean_luminance.cpp         |   4 +-
 src/ipa/libipa/awb_bayes.cpp                  |   4 +-
 src/ipa/mali-c55/algorithms/lsc.cpp           |   6 +-
 src/ipa/rkisp1/algorithms/agc.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/dpf.cpp             |   6 +-
 src/ipa/rkisp1/algorithms/gsl.cpp             |   8 +-
 src/ipa/rkisp1/algorithms/lsc.cpp             |   4 +-
 src/ipa/rpi/controller/rpi/agc_channel.cpp    |   4 +-
 src/ipa/rpi/controller/rpi/hdr.cpp            |   8 +-
 src/libcamera/converter/converter_dw100.cpp   |   2 +-
 src/libcamera/global_configuration.cpp        |   2 +-
 .../pipeline/virtual/config_parser.cpp        |   2 +-
 src/libcamera/yaml_parser.cpp                 | 102 ++++++++----------
 test/yaml-parser.cpp                          |   4 +-
 15 files changed, 75 insertions(+), 102 deletions(-)

Comments

Barnabás Pőcze Jan. 13, 2026, 4:21 p.m. UTC | #1
2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:
> The YamlObject class has two member function templates to get values:
> the get() function gets a scalar value, while the getList() function
> gets a vector of scalar values.
> 
> As get() is a function template, we can provide specializations for
> vector types. This makes the code more systematic, and therefore more
> readable. Replace all getList() occurrences, and drop the getList()
> function.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>   include/libcamera/internal/yaml_parser.h      |  19 ----
>   src/ipa/libipa/agc_mean_luminance.cpp         |   4 +-
>   src/ipa/libipa/awb_bayes.cpp                  |   4 +-
>   src/ipa/mali-c55/algorithms/lsc.cpp           |   6 +-
>   src/ipa/rkisp1/algorithms/agc.cpp             |   2 +-
>   src/ipa/rkisp1/algorithms/dpf.cpp             |   6 +-
>   src/ipa/rkisp1/algorithms/gsl.cpp             |   8 +-
>   src/ipa/rkisp1/algorithms/lsc.cpp             |   4 +-
>   src/ipa/rpi/controller/rpi/agc_channel.cpp    |   4 +-
>   src/ipa/rpi/controller/rpi/hdr.cpp            |   8 +-
>   src/libcamera/converter/converter_dw100.cpp   |   2 +-
>   src/libcamera/global_configuration.cpp        |   2 +-
>   .../pipeline/virtual/config_parser.cpp        |   2 +-
>   src/libcamera/yaml_parser.cpp                 | 102 ++++++++----------
>   test/yaml-parser.cpp                          |   4 +-
>   15 files changed, 75 insertions(+), 102 deletions(-)
> 
> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> index 0ff026706682..7953befe11e2 100644
> --- a/include/libcamera/internal/yaml_parser.h
> +++ b/include/libcamera/internal/yaml_parser.h
> @@ -182,25 +182,6 @@ public:
>   		return get<T>().value_or(std::forward<U>(defaultValue));
>   	}
>   
> -#ifndef __DOXYGEN__
> -	template<typename T,
> -		 std::enable_if_t<
> -			 std::is_same_v<bool, T> ||
> -			 std::is_same_v<float, T> ||
> -			 std::is_same_v<double, T> ||
> -			 std::is_same_v<int8_t, T> ||
> -			 std::is_same_v<uint8_t, T> ||
> -			 std::is_same_v<int16_t, T> ||
> -			 std::is_same_v<uint16_t, T> ||
> -			 std::is_same_v<int32_t, T> ||
> -			 std::is_same_v<uint32_t, T> ||
> -			 std::is_same_v<std::string, T> ||
> -			 std::is_same_v<Size, T>> * = nullptr>
> -#else
> -	template<typename T>
> -#endif
> -	std::optional<std::vector<T>> getList() const;
> -
>   	DictAdapter asDict() const { return DictAdapter{ list_ }; }
>   	ListAdapter asList() const { return ListAdapter{ list_ }; }
>   
> diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp
> index 564d4143d363..72988096d384 100644
> --- a/src/ipa/libipa/agc_mean_luminance.cpp
> +++ b/src/ipa/libipa/agc_mean_luminance.cpp
> @@ -288,9 +288,9 @@ int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData)
>   			}
>   
>   			std::vector<uint32_t> exposureTimes =
> -				modeValues["exposureTime"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
> +				modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(std::vector<uint32_t>{});
>   			std::vector<double> gains =
> -				modeValues["gain"].getList<double>().value_or(std::vector<double>{});
> +				modeValues["gain"].get<std::vector<double>>().value_or(std::vector<double>{});
>   
>   			if (exposureTimes.size() != gains.size()) {
>   				LOG(AgcMeanLuminance, Error)
> diff --git a/src/ipa/libipa/awb_bayes.cpp b/src/ipa/libipa/awb_bayes.cpp
> index d2bcbd83d7f8..595bd0705732 100644
> --- a/src/ipa/libipa/awb_bayes.cpp
> +++ b/src/ipa/libipa/awb_bayes.cpp
> @@ -211,9 +211,9 @@ int AwbBayes::readPriors(const YamlObject &tuningData)
>   		}
>   
>   		std::vector<uint32_t> temperatures =
> -			p["ct"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
> +			p["ct"].get<std::vector<uint32_t>>().value_or(std::vector<uint32_t>{});
>   		std::vector<double> probabilities =
> -			p["probability"].getList<double>().value_or(std::vector<double>{});
> +			p["probability"].get<std::vector<double>>().value_or(std::vector<double>{});
>   
>   		if (temperatures.size() != probabilities.size()) {
>   			LOG(Awb, Error)
> diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp
> index 5b042c757bc7..f75b9cd7b7b8 100644
> --- a/src/ipa/mali-c55/algorithms/lsc.cpp
> +++ b/src/ipa/mali-c55/algorithms/lsc.cpp
> @@ -48,11 +48,11 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
>   		}
>   
>   		std::vector<uint8_t> rTable =
> -			yamlSet["r"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> +			yamlSet["r"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
>   		std::vector<uint8_t> gTable =
> -			yamlSet["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> +			yamlSet["g"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
>   		std::vector<uint8_t> bTable =
> -			yamlSet["b"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> +			yamlSet["b"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
>   
>   		/*
>   		 * Some validation to do; only 16x16 and 32x32 tables of
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index 1ecaff680978..7cc06f91ac2b 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -55,7 +55,7 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData)
>   		}
>   
>   		std::vector<uint8_t> weights =
> -			value.getList<uint8_t>().value_or(std::vector<uint8_t>{});
> +			value.get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
>   		if (weights.size() != context.hw.numHistogramWeights) {
>   			LOG(RkISP1Agc, Warning)
>   				<< "Failed to read metering mode'" << key << "'";
> diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
> index 39f3e461f313..ec989bc2421f 100644
> --- a/src/ipa/rkisp1/algorithms/dpf.cpp
> +++ b/src/ipa/rkisp1/algorithms/dpf.cpp
> @@ -72,7 +72,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context,
>   	 *    +---------|--------> X
>   	 *     -4....-1 0 1 2 3 4
>   	 */
> -	values = dFObject["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> +	values = dFObject["g"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
>   	if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS) {
>   		LOG(RkISP1Dpf, Error)
>   			<< "Invalid 'DomainFilter:g': expected "
> @@ -108,7 +108,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context,
>   	 * For a 9x9 kernel, columns -6 and 6 are dropped, so coefficient
>   	 * number 6 is not used.
>   	 */
> -	values = dFObject["rb"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> +	values = dFObject["rb"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
>   	if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS &&
>   	    values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1) {
>   		LOG(RkISP1Dpf, Error)
> @@ -137,7 +137,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context,
>   	const YamlObject &rFObject = tuningData["NoiseLevelFunction"];
>   
>   	std::vector<uint16_t> nllValues;
> -	nllValues = rFObject["coeff"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> +	nllValues = rFObject["coeff"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
>   	if (nllValues.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) {
>   		LOG(RkISP1Dpf, Error)
>   			<< "Invalid 'RangeFilter:coeff': expected "
> diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp
> index 9604c0ac001a..7ac5dc215850 100644
> --- a/src/ipa/rkisp1/algorithms/gsl.cpp
> +++ b/src/ipa/rkisp1/algorithms/gsl.cpp
> @@ -59,7 +59,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
>   				   const YamlObject &tuningData)
>   {
>   	std::vector<uint16_t> xIntervals =
> -		tuningData["x-intervals"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> +		tuningData["x-intervals"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
>   	if (xIntervals.size() != kDegammaXIntervals) {
>   		LOG(RkISP1Gsl, Error)
>   			<< "Invalid 'x' coordinates: expected "
> @@ -83,7 +83,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
>   		return -EINVAL;
>   	}
>   
> -	curveYr_ = yObject["red"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> +	curveYr_ = yObject["red"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
>   	if (curveYr_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
>   		LOG(RkISP1Gsl, Error)
>   			<< "Invalid 'y:red' coordinates: expected "
> @@ -92,7 +92,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
>   		return -EINVAL;
>   	}
>   
> -	curveYg_ = yObject["green"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> +	curveYg_ = yObject["green"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
>   	if (curveYg_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
>   		LOG(RkISP1Gsl, Error)
>   			<< "Invalid 'y:green' coordinates: expected "
> @@ -101,7 +101,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
>   		return -EINVAL;
>   	}
>   
> -	curveYb_ = yObject["blue"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> +	curveYb_ = yObject["blue"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
>   	if (curveYb_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
>   		LOG(RkISP1Gsl, Error)
>   			<< "Invalid 'y:blue' coordinates: expected "
> diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
> index e7301bfec863..429565a0b51f 100644
> --- a/src/ipa/rkisp1/algorithms/lsc.cpp
> +++ b/src/ipa/rkisp1/algorithms/lsc.cpp
> @@ -252,7 +252,7 @@ private:
>   			RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX;
>   
>   		std::vector<uint16_t> table =
> -			tuningData[prop].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> +			tuningData[prop].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
>   		if (table.size() != kLscNumSamples) {
>   			LOG(RkISP1Lsc, Error)
>   				<< "Invalid '" << prop << "' values: expected "
> @@ -269,7 +269,7 @@ static std::vector<double> parseSizes(const YamlObject &tuningData,
>   				      const char *prop)
>   {
>   	std::vector<double> sizes =
> -		tuningData[prop].getList<double>().value_or(std::vector<double>{});
> +		tuningData[prop].get<std::vector<double>>().value_or(std::vector<double>{});
>   	if (sizes.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
>   		LOG(RkISP1Lsc, Error)
>   			<< "Invalid '" << prop << "' values: expected "
> diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> index 154551dfacc2..c6cf1f8903f1 100644
> --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp
> +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> @@ -66,13 +66,13 @@ readMeteringModes(std::map<std::string, AgcMeteringMode> &metering_modes,
>   
>   int AgcExposureMode::read(const libcamera::YamlObject &params)
>   {
> -	auto value = params["shutter"].getList<double>();
> +	auto value = params["shutter"].get<std::vector<double>>();
>   	if (!value)
>   		return -EINVAL;
>   	std::transform(value->begin(), value->end(), std::back_inserter(exposureTime),
>   		       [](double v) { return v * 1us; });
>   
> -	value = params["gain"].getList<double>();
> +	value = params["gain"].get<std::vector<double>>();
>   	if (!value)
>   		return -EINVAL;
>   	gain = std::move(*value);
> diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp
> index f3da8291bf5d..06400ea79a95 100644
> --- a/src/ipa/rpi/controller/rpi/hdr.cpp
> +++ b/src/ipa/rpi/controller/rpi/hdr.cpp
> @@ -29,7 +29,7 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
>   
>   	if (!params.contains("cadence"))
>   		LOG(RPiHdr, Fatal) << "No cadence for HDR mode " << name;
> -	cadence = params["cadence"].getList<unsigned int>().value();
> +	cadence = params["cadence"].get<std::vector<unsigned int>>().value();
>   	if (cadence.empty())
>   		LOG(RPiHdr, Fatal) << "Empty cadence in HDR mode " << name;
>   
> @@ -69,14 +69,14 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
>   		tonemap = params["tonemap"].get<ipa::Pwl>(ipa::Pwl{});
>   	speed = params["speed"].get<double>(1.0);
>   	if (params.contains("hi_quantile_targets")) {
> -		hiQuantileTargets = params["hi_quantile_targets"].getList<double>().value();
> +		hiQuantileTargets = params["hi_quantile_targets"].get<std::vector<double>>().value();
>   		if (hiQuantileTargets.empty() || hiQuantileTargets.size() % 2)
>   			LOG(RPiHdr, Fatal) << "hi_quantile_targets much be even and non-empty";
>   	} else
>   		hiQuantileTargets = { 0.95, 0.65, 0.5, 0.28, 0.3, 0.25 };
>   	hiQuantileMaxGain = params["hi_quantile_max_gain"].get<double>(1.6);
>   	if (params.contains("quantile_targets")) {
> -		quantileTargets = params["quantile_targets"].getList<double>().value();
> +		quantileTargets = params["quantile_targets"].get<std::vector<double>>().value();
>   		if (quantileTargets.empty() || quantileTargets.size() % 2)
>   			LOG(RPiHdr, Fatal) << "quantile_targets much be even and non-empty";
>   	} else
> @@ -84,7 +84,7 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
>   	powerMin = params["power_min"].get<double>(0.65);
>   	powerMax = params["power_max"].get<double>(1.0);
>   	if (params.contains("contrast_adjustments")) {
> -		contrastAdjustments = params["contrast_adjustments"].getList<double>().value();
> +		contrastAdjustments = params["contrast_adjustments"].get<std::vector<double>>().value();
>   	} else
>   		contrastAdjustments = { 0.5, 0.75 };
>   
> diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp
> index df5155cfdc93..5782cd0b21b7 100644
> --- a/src/libcamera/converter/converter_dw100.cpp
> +++ b/src/libcamera/converter/converter_dw100.cpp
> @@ -126,7 +126,7 @@ int ConverterDW100Module::init(const YamlObject &params)
>   		return -EINVAL;
>   	}
>   
> -	const auto coeffs = coefficients.getList<double>();
> +	const auto coeffs = coefficients.get<std::vector<double>>();
>   	if (!coeffs) {
>   		LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
>   		return -EINVAL;
> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
> index e086246d6856..99d16e7c38c6 100644
> --- a/src/libcamera/global_configuration.cpp
> +++ b/src/libcamera/global_configuration.cpp
> @@ -152,7 +152,7 @@ std::optional<std::vector<std::string>> GlobalConfiguration::listOption(
>   		if (!*c)
>   			return {};
>   	}
> -	return c->getList<std::string>();
> +	return c->get<std::vector<std::string>>();
>   }
>   
>   /**
> diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp
> index 1d3d9ba87ec0..fdc729509371 100644
> --- a/src/libcamera/pipeline/virtual/config_parser.cpp
> +++ b/src/libcamera/pipeline/virtual/config_parser.cpp
> @@ -115,7 +115,7 @@ int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData,
>   			std::vector<int64_t> frameRates;
>   			if (supportedResolution.contains("frame_rates")) {
>   				auto frameRatesList =
> -					supportedResolution["frame_rates"].getList<int>();
> +					supportedResolution["frame_rates"].get<std::vector<int>>();
>   				if (!frameRatesList || (frameRatesList->size() != 1 &&
>   							frameRatesList->size() != 2)) {
>   					LOG(Virtual, Error) << "Invalid frame_rates: either one or two values";
> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> index a21e589fdd86..2b3723287051 100644
> --- a/src/libcamera/yaml_parser.cpp
> +++ b/src/libcamera/yaml_parser.cpp
> @@ -109,12 +109,16 @@ std::size_t YamlObject::size() const
>   
>   /**
>    * \fn template<typename T> YamlObject::get<T>() const
> + * \tparam T Type of the value
>    * \brief Parse the YamlObject as a \a T value
>    *
>    * This function parses the value of the YamlObject as a \a T object, and
>    * returns the value. If parsing fails (usually because the YamlObject doesn't
>    * store a \a T value), std::nullopt is returned.
>    *
> + * If the type \a T is an std::vector, the YamlObject will be parsed as a list
> + * of values.
> + *
>    * \return The YamlObject value, or std::nullopt if parsing failed
>    */
>   
> @@ -127,6 +131,9 @@ std::size_t YamlObject::size() const
>    * returns the value. If parsing fails (usually because the YamlObject doesn't
>    * store a \a T value), the \a defaultValue is returned.
>    *
> + * Unlike the get() function, this overload does not support std::vector for the
> + * type \a T.
> + *
>    * \return The YamlObject value, or \a defaultValue if parsing failed
>    */
>   
> @@ -239,65 +246,50 @@ YamlObject::Accessor<Size>::get(const YamlObject &obj) const
>   	return Size(*width, *height);
>   }
>   
> -#endif /* __DOXYGEN__ */
> -
> -/**
> - * \fn template<typename T> YamlObject::getList<T>() const
> - * \brief Parse the YamlObject as a list of \a T
> - *
> - * This function parses the value of the YamlObject as a list of \a T objects,
> - * and returns the value as a \a std::vector<T>. If parsing fails, std::nullopt
> - * is returned.
> - *
> - * \return The YamlObject value as a std::vector<T>, or std::nullopt if parsing
> - * failed
> - */
> -
> -#ifndef __DOXYGEN__
> -
> -template<typename T,
> -	 std::enable_if_t<
> -		 std::is_same_v<bool, T> ||
> -		 std::is_same_v<float, T> ||
> -		 std::is_same_v<double, T> ||
> -		 std::is_same_v<int8_t, T> ||
> -		 std::is_same_v<uint8_t, T> ||
> -		 std::is_same_v<int16_t, T> ||
> -		 std::is_same_v<uint16_t, T> ||
> -		 std::is_same_v<int32_t, T> ||
> -		 std::is_same_v<uint32_t, T> ||
> -		 std::is_same_v<std::string, T> ||
> -		 std::is_same_v<Size, T>> *>
> -std::optional<std::vector<T>> YamlObject::getList() const
> +template<typename T>
> +struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<
> +	std::is_same_v<bool, T> ||
> +	std::is_same_v<float, T> ||
> +	std::is_same_v<double, T> ||
> +	std::is_same_v<int8_t, T> ||
> +	std::is_same_v<uint8_t, T> ||
> +	std::is_same_v<int16_t, T> ||
> +	std::is_same_v<uint16_t, T> ||
> +	std::is_same_v<int32_t, T> ||
> +	std::is_same_v<uint32_t, T> ||
> +	std::is_same_v<std::string, T> ||
> +	std::is_same_v<Size, T>>>

I think the SFINAE check can be removed, no? The "user" side has no SFINAE
checking, so one gets a linker error either way. If a "wrong" type is used
here, when explicitly instantiating, then I think either it works or there
will be a compilation error.

Or maybe

   std::enable_if_t<std::is_same_v<decltype(std::declval<const YamlObject&>().get<T>), std::optional<T>>>

but it is likely an overkill, especially that the instantiations are very much
controlled in this file.

>   {
> -	if (type_ != Type::List)
> -		return std::nullopt;
> -
> -	std::vector<T> values;
> -	values.reserve(list_.size());
> -
> -	for (const YamlObject &entry : asList()) {
> -		const auto value = entry.get<T>();
> -		if (!value)
> +	std::optional<std::vector<T>> get(const YamlObject &obj) const
> +	{
> +		if (obj.type_ != Type::List)
>   			return std::nullopt;
> -		values.emplace_back(*value);
> +
> +		std::vector<T> values;
> +		values.reserve(obj.list_.size());
> +
> +		for (const YamlObject &entry : obj.asList()) {
> +			const auto value = entry.get<T>();
> +			if (!value)
> +				return std::nullopt;
> +			values.emplace_back(*value);

I think we should take this opportunity to make a small change:

   auto value = ...
   ...
   values.emplace_back(std::move(*value));


> +		}
> +
> +		return values;
>   	}
> +};
>   
> -	return values;
> -}
> -
> -template std::optional<std::vector<bool>> YamlObject::getList<bool>() const;
> -template std::optional<std::vector<float>> YamlObject::getList<float>() const;
> -template std::optional<std::vector<double>> YamlObject::getList<double>() const;
> -template std::optional<std::vector<int8_t>> YamlObject::getList<int8_t>() const;
> -template std::optional<std::vector<uint8_t>> YamlObject::getList<uint8_t>() const;
> -template std::optional<std::vector<int16_t>> YamlObject::getList<int16_t>() const;
> -template std::optional<std::vector<uint16_t>> YamlObject::getList<uint16_t>() const;
> -template std::optional<std::vector<int32_t>> YamlObject::getList<int32_t>() const;
> -template std::optional<std::vector<uint32_t>> YamlObject::getList<uint32_t>() const;
> -template std::optional<std::vector<std::string>> YamlObject::getList<std::string>() const;
> -template std::optional<std::vector<Size>> YamlObject::getList<Size>() const;
> -
> +template struct YamlObject::Accessor<std::vector<bool>>;
> +template struct YamlObject::Accessor<std::vector<float>>;
> +template struct YamlObject::Accessor<std::vector<double>>;
> +template struct YamlObject::Accessor<std::vector<int8_t>>;
> +template struct YamlObject::Accessor<std::vector<uint8_t>>;
> +template struct YamlObject::Accessor<std::vector<int16_t>>;
> +template struct YamlObject::Accessor<std::vector<uint16_t>>;
> +template struct YamlObject::Accessor<std::vector<int32_t>>;
> +template struct YamlObject::Accessor<std::vector<uint32_t>>;
> +template struct YamlObject::Accessor<std::vector<std::string>>;
> +template struct YamlObject::Accessor<std::vector<Size>>;
>   #endif /* __DOXYGEN__ */
>   
>   /**
> diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp
> index 1b22c87b72f1..566401afcab6 100644
> --- a/test/yaml-parser.cpp
> +++ b/test/yaml-parser.cpp
> @@ -587,9 +587,9 @@ protected:
>   			return TestFail;
>   		}
>   
> -		const auto &values = firstElement.getList<uint16_t>();
> +		const auto &values = firstElement.get<std::vector<uint16_t>>();
>   		if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) {
> -			cerr << "getList() failed to return correct vector" << std::endl;
> +			cerr << "get() failed to return correct vector" << std::endl;
>   			return TestFail;
>   		}
>
Laurent Pinchart Jan. 13, 2026, 8:40 p.m. UTC | #2
On Tue, Jan 13, 2026 at 05:21:52PM +0100, Barnabás Pőcze wrote:
> 2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:
> > The YamlObject class has two member function templates to get values:
> > the get() function gets a scalar value, while the getList() function
> > gets a vector of scalar values.
> > 
> > As get() is a function template, we can provide specializations for
> > vector types. This makes the code more systematic, and therefore more
> > readable. Replace all getList() occurrences, and drop the getList()
> > function.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >   include/libcamera/internal/yaml_parser.h      |  19 ----
> >   src/ipa/libipa/agc_mean_luminance.cpp         |   4 +-
> >   src/ipa/libipa/awb_bayes.cpp                  |   4 +-
> >   src/ipa/mali-c55/algorithms/lsc.cpp           |   6 +-
> >   src/ipa/rkisp1/algorithms/agc.cpp             |   2 +-
> >   src/ipa/rkisp1/algorithms/dpf.cpp             |   6 +-
> >   src/ipa/rkisp1/algorithms/gsl.cpp             |   8 +-
> >   src/ipa/rkisp1/algorithms/lsc.cpp             |   4 +-
> >   src/ipa/rpi/controller/rpi/agc_channel.cpp    |   4 +-
> >   src/ipa/rpi/controller/rpi/hdr.cpp            |   8 +-
> >   src/libcamera/converter/converter_dw100.cpp   |   2 +-
> >   src/libcamera/global_configuration.cpp        |   2 +-
> >   .../pipeline/virtual/config_parser.cpp        |   2 +-
> >   src/libcamera/yaml_parser.cpp                 | 102 ++++++++----------
> >   test/yaml-parser.cpp                          |   4 +-
> >   15 files changed, 75 insertions(+), 102 deletions(-)
> > 
> > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> > index 0ff026706682..7953befe11e2 100644
> > --- a/include/libcamera/internal/yaml_parser.h
> > +++ b/include/libcamera/internal/yaml_parser.h
> > @@ -182,25 +182,6 @@ public:
> >   		return get<T>().value_or(std::forward<U>(defaultValue));
> >   	}
> >   
> > -#ifndef __DOXYGEN__
> > -	template<typename T,
> > -		 std::enable_if_t<
> > -			 std::is_same_v<bool, T> ||
> > -			 std::is_same_v<float, T> ||
> > -			 std::is_same_v<double, T> ||
> > -			 std::is_same_v<int8_t, T> ||
> > -			 std::is_same_v<uint8_t, T> ||
> > -			 std::is_same_v<int16_t, T> ||
> > -			 std::is_same_v<uint16_t, T> ||
> > -			 std::is_same_v<int32_t, T> ||
> > -			 std::is_same_v<uint32_t, T> ||
> > -			 std::is_same_v<std::string, T> ||
> > -			 std::is_same_v<Size, T>> * = nullptr>
> > -#else
> > -	template<typename T>
> > -#endif
> > -	std::optional<std::vector<T>> getList() const;
> > -
> >   	DictAdapter asDict() const { return DictAdapter{ list_ }; }
> >   	ListAdapter asList() const { return ListAdapter{ list_ }; }
> >   
> > diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp
> > index 564d4143d363..72988096d384 100644
> > --- a/src/ipa/libipa/agc_mean_luminance.cpp
> > +++ b/src/ipa/libipa/agc_mean_luminance.cpp
> > @@ -288,9 +288,9 @@ int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData)
> >   			}
> >   
> >   			std::vector<uint32_t> exposureTimes =
> > -				modeValues["exposureTime"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
> > +				modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(std::vector<uint32_t>{});
> >   			std::vector<double> gains =
> > -				modeValues["gain"].getList<double>().value_or(std::vector<double>{});
> > +				modeValues["gain"].get<std::vector<double>>().value_or(std::vector<double>{});
> >   
> >   			if (exposureTimes.size() != gains.size()) {
> >   				LOG(AgcMeanLuminance, Error)
> > diff --git a/src/ipa/libipa/awb_bayes.cpp b/src/ipa/libipa/awb_bayes.cpp
> > index d2bcbd83d7f8..595bd0705732 100644
> > --- a/src/ipa/libipa/awb_bayes.cpp
> > +++ b/src/ipa/libipa/awb_bayes.cpp
> > @@ -211,9 +211,9 @@ int AwbBayes::readPriors(const YamlObject &tuningData)
> >   		}
> >   
> >   		std::vector<uint32_t> temperatures =
> > -			p["ct"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
> > +			p["ct"].get<std::vector<uint32_t>>().value_or(std::vector<uint32_t>{});
> >   		std::vector<double> probabilities =
> > -			p["probability"].getList<double>().value_or(std::vector<double>{});
> > +			p["probability"].get<std::vector<double>>().value_or(std::vector<double>{});
> >   
> >   		if (temperatures.size() != probabilities.size()) {
> >   			LOG(Awb, Error)
> > diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp
> > index 5b042c757bc7..f75b9cd7b7b8 100644
> > --- a/src/ipa/mali-c55/algorithms/lsc.cpp
> > +++ b/src/ipa/mali-c55/algorithms/lsc.cpp
> > @@ -48,11 +48,11 @@ int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
> >   		}
> >   
> >   		std::vector<uint8_t> rTable =
> > -			yamlSet["r"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> > +			yamlSet["r"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
> >   		std::vector<uint8_t> gTable =
> > -			yamlSet["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> > +			yamlSet["g"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
> >   		std::vector<uint8_t> bTable =
> > -			yamlSet["b"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> > +			yamlSet["b"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
> >   
> >   		/*
> >   		 * Some validation to do; only 16x16 and 32x32 tables of
> > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> > index 1ecaff680978..7cc06f91ac2b 100644
> > --- a/src/ipa/rkisp1/algorithms/agc.cpp
> > +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> > @@ -55,7 +55,7 @@ int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData)
> >   		}
> >   
> >   		std::vector<uint8_t> weights =
> > -			value.getList<uint8_t>().value_or(std::vector<uint8_t>{});
> > +			value.get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
> >   		if (weights.size() != context.hw.numHistogramWeights) {
> >   			LOG(RkISP1Agc, Warning)
> >   				<< "Failed to read metering mode'" << key << "'";
> > diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
> > index 39f3e461f313..ec989bc2421f 100644
> > --- a/src/ipa/rkisp1/algorithms/dpf.cpp
> > +++ b/src/ipa/rkisp1/algorithms/dpf.cpp
> > @@ -72,7 +72,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context,
> >   	 *    +---------|--------> X
> >   	 *     -4....-1 0 1 2 3 4
> >   	 */
> > -	values = dFObject["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> > +	values = dFObject["g"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
> >   	if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS) {
> >   		LOG(RkISP1Dpf, Error)
> >   			<< "Invalid 'DomainFilter:g': expected "
> > @@ -108,7 +108,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context,
> >   	 * For a 9x9 kernel, columns -6 and 6 are dropped, so coefficient
> >   	 * number 6 is not used.
> >   	 */
> > -	values = dFObject["rb"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
> > +	values = dFObject["rb"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
> >   	if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS &&
> >   	    values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1) {
> >   		LOG(RkISP1Dpf, Error)
> > @@ -137,7 +137,7 @@ int Dpf::init([[maybe_unused]] IPAContext &context,
> >   	const YamlObject &rFObject = tuningData["NoiseLevelFunction"];
> >   
> >   	std::vector<uint16_t> nllValues;
> > -	nllValues = rFObject["coeff"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> > +	nllValues = rFObject["coeff"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
> >   	if (nllValues.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) {
> >   		LOG(RkISP1Dpf, Error)
> >   			<< "Invalid 'RangeFilter:coeff': expected "
> > diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp
> > index 9604c0ac001a..7ac5dc215850 100644
> > --- a/src/ipa/rkisp1/algorithms/gsl.cpp
> > +++ b/src/ipa/rkisp1/algorithms/gsl.cpp
> > @@ -59,7 +59,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
> >   				   const YamlObject &tuningData)
> >   {
> >   	std::vector<uint16_t> xIntervals =
> > -		tuningData["x-intervals"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> > +		tuningData["x-intervals"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
> >   	if (xIntervals.size() != kDegammaXIntervals) {
> >   		LOG(RkISP1Gsl, Error)
> >   			<< "Invalid 'x' coordinates: expected "
> > @@ -83,7 +83,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
> >   		return -EINVAL;
> >   	}
> >   
> > -	curveYr_ = yObject["red"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> > +	curveYr_ = yObject["red"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
> >   	if (curveYr_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
> >   		LOG(RkISP1Gsl, Error)
> >   			<< "Invalid 'y:red' coordinates: expected "
> > @@ -92,7 +92,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
> >   		return -EINVAL;
> >   	}
> >   
> > -	curveYg_ = yObject["green"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> > +	curveYg_ = yObject["green"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
> >   	if (curveYg_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
> >   		LOG(RkISP1Gsl, Error)
> >   			<< "Invalid 'y:green' coordinates: expected "
> > @@ -101,7 +101,7 @@ int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
> >   		return -EINVAL;
> >   	}
> >   
> > -	curveYb_ = yObject["blue"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> > +	curveYb_ = yObject["blue"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
> >   	if (curveYb_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
> >   		LOG(RkISP1Gsl, Error)
> >   			<< "Invalid 'y:blue' coordinates: expected "
> > diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
> > index e7301bfec863..429565a0b51f 100644
> > --- a/src/ipa/rkisp1/algorithms/lsc.cpp
> > +++ b/src/ipa/rkisp1/algorithms/lsc.cpp
> > @@ -252,7 +252,7 @@ private:
> >   			RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX;
> >   
> >   		std::vector<uint16_t> table =
> > -			tuningData[prop].getList<uint16_t>().value_or(std::vector<uint16_t>{});
> > +			tuningData[prop].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
> >   		if (table.size() != kLscNumSamples) {
> >   			LOG(RkISP1Lsc, Error)
> >   				<< "Invalid '" << prop << "' values: expected "
> > @@ -269,7 +269,7 @@ static std::vector<double> parseSizes(const YamlObject &tuningData,
> >   				      const char *prop)
> >   {
> >   	std::vector<double> sizes =
> > -		tuningData[prop].getList<double>().value_or(std::vector<double>{});
> > +		tuningData[prop].get<std::vector<double>>().value_or(std::vector<double>{});
> >   	if (sizes.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> >   		LOG(RkISP1Lsc, Error)
> >   			<< "Invalid '" << prop << "' values: expected "
> > diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> > index 154551dfacc2..c6cf1f8903f1 100644
> > --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp
> > +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> > @@ -66,13 +66,13 @@ readMeteringModes(std::map<std::string, AgcMeteringMode> &metering_modes,
> >   
> >   int AgcExposureMode::read(const libcamera::YamlObject &params)
> >   {
> > -	auto value = params["shutter"].getList<double>();
> > +	auto value = params["shutter"].get<std::vector<double>>();
> >   	if (!value)
> >   		return -EINVAL;
> >   	std::transform(value->begin(), value->end(), std::back_inserter(exposureTime),
> >   		       [](double v) { return v * 1us; });
> >   
> > -	value = params["gain"].getList<double>();
> > +	value = params["gain"].get<std::vector<double>>();
> >   	if (!value)
> >   		return -EINVAL;
> >   	gain = std::move(*value);
> > diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp
> > index f3da8291bf5d..06400ea79a95 100644
> > --- a/src/ipa/rpi/controller/rpi/hdr.cpp
> > +++ b/src/ipa/rpi/controller/rpi/hdr.cpp
> > @@ -29,7 +29,7 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
> >   
> >   	if (!params.contains("cadence"))
> >   		LOG(RPiHdr, Fatal) << "No cadence for HDR mode " << name;
> > -	cadence = params["cadence"].getList<unsigned int>().value();
> > +	cadence = params["cadence"].get<std::vector<unsigned int>>().value();
> >   	if (cadence.empty())
> >   		LOG(RPiHdr, Fatal) << "Empty cadence in HDR mode " << name;
> >   
> > @@ -69,14 +69,14 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
> >   		tonemap = params["tonemap"].get<ipa::Pwl>(ipa::Pwl{});
> >   	speed = params["speed"].get<double>(1.0);
> >   	if (params.contains("hi_quantile_targets")) {
> > -		hiQuantileTargets = params["hi_quantile_targets"].getList<double>().value();
> > +		hiQuantileTargets = params["hi_quantile_targets"].get<std::vector<double>>().value();
> >   		if (hiQuantileTargets.empty() || hiQuantileTargets.size() % 2)
> >   			LOG(RPiHdr, Fatal) << "hi_quantile_targets much be even and non-empty";
> >   	} else
> >   		hiQuantileTargets = { 0.95, 0.65, 0.5, 0.28, 0.3, 0.25 };
> >   	hiQuantileMaxGain = params["hi_quantile_max_gain"].get<double>(1.6);
> >   	if (params.contains("quantile_targets")) {
> > -		quantileTargets = params["quantile_targets"].getList<double>().value();
> > +		quantileTargets = params["quantile_targets"].get<std::vector<double>>().value();
> >   		if (quantileTargets.empty() || quantileTargets.size() % 2)
> >   			LOG(RPiHdr, Fatal) << "quantile_targets much be even and non-empty";
> >   	} else
> > @@ -84,7 +84,7 @@ void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
> >   	powerMin = params["power_min"].get<double>(0.65);
> >   	powerMax = params["power_max"].get<double>(1.0);
> >   	if (params.contains("contrast_adjustments")) {
> > -		contrastAdjustments = params["contrast_adjustments"].getList<double>().value();
> > +		contrastAdjustments = params["contrast_adjustments"].get<std::vector<double>>().value();
> >   	} else
> >   		contrastAdjustments = { 0.5, 0.75 };
> >   
> > diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp
> > index df5155cfdc93..5782cd0b21b7 100644
> > --- a/src/libcamera/converter/converter_dw100.cpp
> > +++ b/src/libcamera/converter/converter_dw100.cpp
> > @@ -126,7 +126,7 @@ int ConverterDW100Module::init(const YamlObject &params)
> >   		return -EINVAL;
> >   	}
> >   
> > -	const auto coeffs = coefficients.getList<double>();
> > +	const auto coeffs = coefficients.get<std::vector<double>>();
> >   	if (!coeffs) {
> >   		LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
> >   		return -EINVAL;
> > diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
> > index e086246d6856..99d16e7c38c6 100644
> > --- a/src/libcamera/global_configuration.cpp
> > +++ b/src/libcamera/global_configuration.cpp
> > @@ -152,7 +152,7 @@ std::optional<std::vector<std::string>> GlobalConfiguration::listOption(
> >   		if (!*c)
> >   			return {};
> >   	}
> > -	return c->getList<std::string>();
> > +	return c->get<std::vector<std::string>>();
> >   }
> >   
> >   /**
> > diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp
> > index 1d3d9ba87ec0..fdc729509371 100644
> > --- a/src/libcamera/pipeline/virtual/config_parser.cpp
> > +++ b/src/libcamera/pipeline/virtual/config_parser.cpp
> > @@ -115,7 +115,7 @@ int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData,
> >   			std::vector<int64_t> frameRates;
> >   			if (supportedResolution.contains("frame_rates")) {
> >   				auto frameRatesList =
> > -					supportedResolution["frame_rates"].getList<int>();
> > +					supportedResolution["frame_rates"].get<std::vector<int>>();
> >   				if (!frameRatesList || (frameRatesList->size() != 1 &&
> >   							frameRatesList->size() != 2)) {
> >   					LOG(Virtual, Error) << "Invalid frame_rates: either one or two values";
> > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> > index a21e589fdd86..2b3723287051 100644
> > --- a/src/libcamera/yaml_parser.cpp
> > +++ b/src/libcamera/yaml_parser.cpp
> > @@ -109,12 +109,16 @@ std::size_t YamlObject::size() const
> >   
> >   /**
> >    * \fn template<typename T> YamlObject::get<T>() const
> > + * \tparam T Type of the value
> >    * \brief Parse the YamlObject as a \a T value
> >    *
> >    * This function parses the value of the YamlObject as a \a T object, and
> >    * returns the value. If parsing fails (usually because the YamlObject doesn't
> >    * store a \a T value), std::nullopt is returned.
> >    *
> > + * If the type \a T is an std::vector, the YamlObject will be parsed as a list
> > + * of values.
> > + *
> >    * \return The YamlObject value, or std::nullopt if parsing failed
> >    */
> >   
> > @@ -127,6 +131,9 @@ std::size_t YamlObject::size() const
> >    * returns the value. If parsing fails (usually because the YamlObject doesn't
> >    * store a \a T value), the \a defaultValue is returned.
> >    *
> > + * Unlike the get() function, this overload does not support std::vector for the
> > + * type \a T.
> > + *
> >    * \return The YamlObject value, or \a defaultValue if parsing failed
> >    */
> >   
> > @@ -239,65 +246,50 @@ YamlObject::Accessor<Size>::get(const YamlObject &obj) const
> >   	return Size(*width, *height);
> >   }
> >   
> > -#endif /* __DOXYGEN__ */
> > -
> > -/**
> > - * \fn template<typename T> YamlObject::getList<T>() const
> > - * \brief Parse the YamlObject as a list of \a T
> > - *
> > - * This function parses the value of the YamlObject as a list of \a T objects,
> > - * and returns the value as a \a std::vector<T>. If parsing fails, std::nullopt
> > - * is returned.
> > - *
> > - * \return The YamlObject value as a std::vector<T>, or std::nullopt if parsing
> > - * failed
> > - */
> > -
> > -#ifndef __DOXYGEN__
> > -
> > -template<typename T,
> > -	 std::enable_if_t<
> > -		 std::is_same_v<bool, T> ||
> > -		 std::is_same_v<float, T> ||
> > -		 std::is_same_v<double, T> ||
> > -		 std::is_same_v<int8_t, T> ||
> > -		 std::is_same_v<uint8_t, T> ||
> > -		 std::is_same_v<int16_t, T> ||
> > -		 std::is_same_v<uint16_t, T> ||
> > -		 std::is_same_v<int32_t, T> ||
> > -		 std::is_same_v<uint32_t, T> ||
> > -		 std::is_same_v<std::string, T> ||
> > -		 std::is_same_v<Size, T>> *>
> > -std::optional<std::vector<T>> YamlObject::getList() const
> > +template<typename T>
> > +struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<
> > +	std::is_same_v<bool, T> ||
> > +	std::is_same_v<float, T> ||
> > +	std::is_same_v<double, T> ||
> > +	std::is_same_v<int8_t, T> ||
> > +	std::is_same_v<uint8_t, T> ||
> > +	std::is_same_v<int16_t, T> ||
> > +	std::is_same_v<uint16_t, T> ||
> > +	std::is_same_v<int32_t, T> ||
> > +	std::is_same_v<uint32_t, T> ||
> > +	std::is_same_v<std::string, T> ||
> > +	std::is_same_v<Size, T>>>
> 
> I think the SFINAE check can be removed, no? The "user" side has no SFINAE
> checking, so one gets a linker error either way. If a "wrong" type is used
> here, when explicitly instantiating, then I think either it works or there
> will be a compilation error.

Indeed. I'll try to drop it.

> Or maybe
> 
>    std::enable_if_t<std::is_same_v<decltype(std::declval<const YamlObject&>().get<T>), std::optional<T>>>
> 
> but it is likely an overkill, especially that the instantiations are very much
> controlled in this file.
> 
> >   {
> > -	if (type_ != Type::List)
> > -		return std::nullopt;
> > -
> > -	std::vector<T> values;
> > -	values.reserve(list_.size());
> > -
> > -	for (const YamlObject &entry : asList()) {
> > -		const auto value = entry.get<T>();
> > -		if (!value)
> > +	std::optional<std::vector<T>> get(const YamlObject &obj) const
> > +	{
> > +		if (obj.type_ != Type::List)
> >   			return std::nullopt;
> > -		values.emplace_back(*value);
> > +
> > +		std::vector<T> values;
> > +		values.reserve(obj.list_.size());
> > +
> > +		for (const YamlObject &entry : obj.asList()) {
> > +			const auto value = entry.get<T>();
> > +			if (!value)
> > +				return std::nullopt;
> > +			values.emplace_back(*value);
> 
> I think we should take this opportunity to make a small change:
> 
>    auto value = ...
>    ...
>    values.emplace_back(std::move(*value));

Ack.

> > +		}
> > +
> > +		return values;
> >   	}
> > +};
> >   
> > -	return values;
> > -}
> > -
> > -template std::optional<std::vector<bool>> YamlObject::getList<bool>() const;
> > -template std::optional<std::vector<float>> YamlObject::getList<float>() const;
> > -template std::optional<std::vector<double>> YamlObject::getList<double>() const;
> > -template std::optional<std::vector<int8_t>> YamlObject::getList<int8_t>() const;
> > -template std::optional<std::vector<uint8_t>> YamlObject::getList<uint8_t>() const;
> > -template std::optional<std::vector<int16_t>> YamlObject::getList<int16_t>() const;
> > -template std::optional<std::vector<uint16_t>> YamlObject::getList<uint16_t>() const;
> > -template std::optional<std::vector<int32_t>> YamlObject::getList<int32_t>() const;
> > -template std::optional<std::vector<uint32_t>> YamlObject::getList<uint32_t>() const;
> > -template std::optional<std::vector<std::string>> YamlObject::getList<std::string>() const;
> > -template std::optional<std::vector<Size>> YamlObject::getList<Size>() const;
> > -
> > +template struct YamlObject::Accessor<std::vector<bool>>;
> > +template struct YamlObject::Accessor<std::vector<float>>;
> > +template struct YamlObject::Accessor<std::vector<double>>;
> > +template struct YamlObject::Accessor<std::vector<int8_t>>;
> > +template struct YamlObject::Accessor<std::vector<uint8_t>>;
> > +template struct YamlObject::Accessor<std::vector<int16_t>>;
> > +template struct YamlObject::Accessor<std::vector<uint16_t>>;
> > +template struct YamlObject::Accessor<std::vector<int32_t>>;
> > +template struct YamlObject::Accessor<std::vector<uint32_t>>;
> > +template struct YamlObject::Accessor<std::vector<std::string>>;
> > +template struct YamlObject::Accessor<std::vector<Size>>;
> >   #endif /* __DOXYGEN__ */
> >   
> >   /**
> > diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp
> > index 1b22c87b72f1..566401afcab6 100644
> > --- a/test/yaml-parser.cpp
> > +++ b/test/yaml-parser.cpp
> > @@ -587,9 +587,9 @@ protected:
> >   			return TestFail;
> >   		}
> >   
> > -		const auto &values = firstElement.getList<uint16_t>();
> > +		const auto &values = firstElement.get<std::vector<uint16_t>>();
> >   		if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) {
> > -			cerr << "getList() failed to return correct vector" << std::endl;
> > +			cerr << "get() failed to return correct vector" << std::endl;
> >   			return TestFail;
> >   		}
> >

Patch
diff mbox series

diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
index 0ff026706682..7953befe11e2 100644
--- a/include/libcamera/internal/yaml_parser.h
+++ b/include/libcamera/internal/yaml_parser.h
@@ -182,25 +182,6 @@  public:
 		return get<T>().value_or(std::forward<U>(defaultValue));
 	}
 
-#ifndef __DOXYGEN__
-	template<typename T,
-		 std::enable_if_t<
-			 std::is_same_v<bool, T> ||
-			 std::is_same_v<float, T> ||
-			 std::is_same_v<double, T> ||
-			 std::is_same_v<int8_t, T> ||
-			 std::is_same_v<uint8_t, T> ||
-			 std::is_same_v<int16_t, T> ||
-			 std::is_same_v<uint16_t, T> ||
-			 std::is_same_v<int32_t, T> ||
-			 std::is_same_v<uint32_t, T> ||
-			 std::is_same_v<std::string, T> ||
-			 std::is_same_v<Size, T>> * = nullptr>
-#else
-	template<typename T>
-#endif
-	std::optional<std::vector<T>> getList() const;
-
 	DictAdapter asDict() const { return DictAdapter{ list_ }; }
 	ListAdapter asList() const { return ListAdapter{ list_ }; }
 
diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp
index 564d4143d363..72988096d384 100644
--- a/src/ipa/libipa/agc_mean_luminance.cpp
+++ b/src/ipa/libipa/agc_mean_luminance.cpp
@@ -288,9 +288,9 @@  int AgcMeanLuminance::parseExposureModes(const YamlObject &tuningData)
 			}
 
 			std::vector<uint32_t> exposureTimes =
-				modeValues["exposureTime"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
+				modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(std::vector<uint32_t>{});
 			std::vector<double> gains =
-				modeValues["gain"].getList<double>().value_or(std::vector<double>{});
+				modeValues["gain"].get<std::vector<double>>().value_or(std::vector<double>{});
 
 			if (exposureTimes.size() != gains.size()) {
 				LOG(AgcMeanLuminance, Error)
diff --git a/src/ipa/libipa/awb_bayes.cpp b/src/ipa/libipa/awb_bayes.cpp
index d2bcbd83d7f8..595bd0705732 100644
--- a/src/ipa/libipa/awb_bayes.cpp
+++ b/src/ipa/libipa/awb_bayes.cpp
@@ -211,9 +211,9 @@  int AwbBayes::readPriors(const YamlObject &tuningData)
 		}
 
 		std::vector<uint32_t> temperatures =
-			p["ct"].getList<uint32_t>().value_or(std::vector<uint32_t>{});
+			p["ct"].get<std::vector<uint32_t>>().value_or(std::vector<uint32_t>{});
 		std::vector<double> probabilities =
-			p["probability"].getList<double>().value_or(std::vector<double>{});
+			p["probability"].get<std::vector<double>>().value_or(std::vector<double>{});
 
 		if (temperatures.size() != probabilities.size()) {
 			LOG(Awb, Error)
diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp
index 5b042c757bc7..f75b9cd7b7b8 100644
--- a/src/ipa/mali-c55/algorithms/lsc.cpp
+++ b/src/ipa/mali-c55/algorithms/lsc.cpp
@@ -48,11 +48,11 @@  int Lsc::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
 		}
 
 		std::vector<uint8_t> rTable =
-			yamlSet["r"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+			yamlSet["r"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
 		std::vector<uint8_t> gTable =
-			yamlSet["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+			yamlSet["g"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
 		std::vector<uint8_t> bTable =
-			yamlSet["b"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+			yamlSet["b"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
 
 		/*
 		 * Some validation to do; only 16x16 and 32x32 tables of
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index 1ecaff680978..7cc06f91ac2b 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -55,7 +55,7 @@  int Agc::parseMeteringModes(IPAContext &context, const YamlObject &tuningData)
 		}
 
 		std::vector<uint8_t> weights =
-			value.getList<uint8_t>().value_or(std::vector<uint8_t>{});
+			value.get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
 		if (weights.size() != context.hw.numHistogramWeights) {
 			LOG(RkISP1Agc, Warning)
 				<< "Failed to read metering mode'" << key << "'";
diff --git a/src/ipa/rkisp1/algorithms/dpf.cpp b/src/ipa/rkisp1/algorithms/dpf.cpp
index 39f3e461f313..ec989bc2421f 100644
--- a/src/ipa/rkisp1/algorithms/dpf.cpp
+++ b/src/ipa/rkisp1/algorithms/dpf.cpp
@@ -72,7 +72,7 @@  int Dpf::init([[maybe_unused]] IPAContext &context,
 	 *    +---------|--------> X
 	 *     -4....-1 0 1 2 3 4
 	 */
-	values = dFObject["g"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+	values = dFObject["g"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
 	if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS) {
 		LOG(RkISP1Dpf, Error)
 			<< "Invalid 'DomainFilter:g': expected "
@@ -108,7 +108,7 @@  int Dpf::init([[maybe_unused]] IPAContext &context,
 	 * For a 9x9 kernel, columns -6 and 6 are dropped, so coefficient
 	 * number 6 is not used.
 	 */
-	values = dFObject["rb"].getList<uint8_t>().value_or(std::vector<uint8_t>{});
+	values = dFObject["rb"].get<std::vector<uint8_t>>().value_or(std::vector<uint8_t>{});
 	if (values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS &&
 	    values.size() != RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS - 1) {
 		LOG(RkISP1Dpf, Error)
@@ -137,7 +137,7 @@  int Dpf::init([[maybe_unused]] IPAContext &context,
 	const YamlObject &rFObject = tuningData["NoiseLevelFunction"];
 
 	std::vector<uint16_t> nllValues;
-	nllValues = rFObject["coeff"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+	nllValues = rFObject["coeff"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
 	if (nllValues.size() != RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS) {
 		LOG(RkISP1Dpf, Error)
 			<< "Invalid 'RangeFilter:coeff': expected "
diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp
index 9604c0ac001a..7ac5dc215850 100644
--- a/src/ipa/rkisp1/algorithms/gsl.cpp
+++ b/src/ipa/rkisp1/algorithms/gsl.cpp
@@ -59,7 +59,7 @@  int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
 				   const YamlObject &tuningData)
 {
 	std::vector<uint16_t> xIntervals =
-		tuningData["x-intervals"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+		tuningData["x-intervals"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
 	if (xIntervals.size() != kDegammaXIntervals) {
 		LOG(RkISP1Gsl, Error)
 			<< "Invalid 'x' coordinates: expected "
@@ -83,7 +83,7 @@  int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
 		return -EINVAL;
 	}
 
-	curveYr_ = yObject["red"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+	curveYr_ = yObject["red"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
 	if (curveYr_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
 		LOG(RkISP1Gsl, Error)
 			<< "Invalid 'y:red' coordinates: expected "
@@ -92,7 +92,7 @@  int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
 		return -EINVAL;
 	}
 
-	curveYg_ = yObject["green"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+	curveYg_ = yObject["green"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
 	if (curveYg_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
 		LOG(RkISP1Gsl, Error)
 			<< "Invalid 'y:green' coordinates: expected "
@@ -101,7 +101,7 @@  int GammaSensorLinearization::init([[maybe_unused]] IPAContext &context,
 		return -EINVAL;
 	}
 
-	curveYb_ = yObject["blue"].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+	curveYb_ = yObject["blue"].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
 	if (curveYb_.size() != RKISP1_CIF_ISP_DEGAMMA_CURVE_SIZE) {
 		LOG(RkISP1Gsl, Error)
 			<< "Invalid 'y:blue' coordinates: expected "
diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
index e7301bfec863..429565a0b51f 100644
--- a/src/ipa/rkisp1/algorithms/lsc.cpp
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -252,7 +252,7 @@  private:
 			RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX;
 
 		std::vector<uint16_t> table =
-			tuningData[prop].getList<uint16_t>().value_or(std::vector<uint16_t>{});
+			tuningData[prop].get<std::vector<uint16_t>>().value_or(std::vector<uint16_t>{});
 		if (table.size() != kLscNumSamples) {
 			LOG(RkISP1Lsc, Error)
 				<< "Invalid '" << prop << "' values: expected "
@@ -269,7 +269,7 @@  static std::vector<double> parseSizes(const YamlObject &tuningData,
 				      const char *prop)
 {
 	std::vector<double> sizes =
-		tuningData[prop].getList<double>().value_or(std::vector<double>{});
+		tuningData[prop].get<std::vector<double>>().value_or(std::vector<double>{});
 	if (sizes.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
 		LOG(RkISP1Lsc, Error)
 			<< "Invalid '" << prop << "' values: expected "
diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp
index 154551dfacc2..c6cf1f8903f1 100644
--- a/src/ipa/rpi/controller/rpi/agc_channel.cpp
+++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp
@@ -66,13 +66,13 @@  readMeteringModes(std::map<std::string, AgcMeteringMode> &metering_modes,
 
 int AgcExposureMode::read(const libcamera::YamlObject &params)
 {
-	auto value = params["shutter"].getList<double>();
+	auto value = params["shutter"].get<std::vector<double>>();
 	if (!value)
 		return -EINVAL;
 	std::transform(value->begin(), value->end(), std::back_inserter(exposureTime),
 		       [](double v) { return v * 1us; });
 
-	value = params["gain"].getList<double>();
+	value = params["gain"].get<std::vector<double>>();
 	if (!value)
 		return -EINVAL;
 	gain = std::move(*value);
diff --git a/src/ipa/rpi/controller/rpi/hdr.cpp b/src/ipa/rpi/controller/rpi/hdr.cpp
index f3da8291bf5d..06400ea79a95 100644
--- a/src/ipa/rpi/controller/rpi/hdr.cpp
+++ b/src/ipa/rpi/controller/rpi/hdr.cpp
@@ -29,7 +29,7 @@  void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
 
 	if (!params.contains("cadence"))
 		LOG(RPiHdr, Fatal) << "No cadence for HDR mode " << name;
-	cadence = params["cadence"].getList<unsigned int>().value();
+	cadence = params["cadence"].get<std::vector<unsigned int>>().value();
 	if (cadence.empty())
 		LOG(RPiHdr, Fatal) << "Empty cadence in HDR mode " << name;
 
@@ -69,14 +69,14 @@  void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
 		tonemap = params["tonemap"].get<ipa::Pwl>(ipa::Pwl{});
 	speed = params["speed"].get<double>(1.0);
 	if (params.contains("hi_quantile_targets")) {
-		hiQuantileTargets = params["hi_quantile_targets"].getList<double>().value();
+		hiQuantileTargets = params["hi_quantile_targets"].get<std::vector<double>>().value();
 		if (hiQuantileTargets.empty() || hiQuantileTargets.size() % 2)
 			LOG(RPiHdr, Fatal) << "hi_quantile_targets much be even and non-empty";
 	} else
 		hiQuantileTargets = { 0.95, 0.65, 0.5, 0.28, 0.3, 0.25 };
 	hiQuantileMaxGain = params["hi_quantile_max_gain"].get<double>(1.6);
 	if (params.contains("quantile_targets")) {
-		quantileTargets = params["quantile_targets"].getList<double>().value();
+		quantileTargets = params["quantile_targets"].get<std::vector<double>>().value();
 		if (quantileTargets.empty() || quantileTargets.size() % 2)
 			LOG(RPiHdr, Fatal) << "quantile_targets much be even and non-empty";
 	} else
@@ -84,7 +84,7 @@  void HdrConfig::read(const libcamera::YamlObject &params, const std::string &mod
 	powerMin = params["power_min"].get<double>(0.65);
 	powerMax = params["power_max"].get<double>(1.0);
 	if (params.contains("contrast_adjustments")) {
-		contrastAdjustments = params["contrast_adjustments"].getList<double>().value();
+		contrastAdjustments = params["contrast_adjustments"].get<std::vector<double>>().value();
 	} else
 		contrastAdjustments = { 0.5, 0.75 };
 
diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp
index df5155cfdc93..5782cd0b21b7 100644
--- a/src/libcamera/converter/converter_dw100.cpp
+++ b/src/libcamera/converter/converter_dw100.cpp
@@ -126,7 +126,7 @@  int ConverterDW100Module::init(const YamlObject &params)
 		return -EINVAL;
 	}
 
-	const auto coeffs = coefficients.getList<double>();
+	const auto coeffs = coefficients.get<std::vector<double>>();
 	if (!coeffs) {
 		LOG(Converter, Error) << "Dewarp parameters 'coefficients' value is not a list";
 		return -EINVAL;
diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
index e086246d6856..99d16e7c38c6 100644
--- a/src/libcamera/global_configuration.cpp
+++ b/src/libcamera/global_configuration.cpp
@@ -152,7 +152,7 @@  std::optional<std::vector<std::string>> GlobalConfiguration::listOption(
 		if (!*c)
 			return {};
 	}
-	return c->getList<std::string>();
+	return c->get<std::vector<std::string>>();
 }
 
 /**
diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp
index 1d3d9ba87ec0..fdc729509371 100644
--- a/src/libcamera/pipeline/virtual/config_parser.cpp
+++ b/src/libcamera/pipeline/virtual/config_parser.cpp
@@ -115,7 +115,7 @@  int ConfigParser::parseSupportedFormats(const YamlObject &cameraConfigData,
 			std::vector<int64_t> frameRates;
 			if (supportedResolution.contains("frame_rates")) {
 				auto frameRatesList =
-					supportedResolution["frame_rates"].getList<int>();
+					supportedResolution["frame_rates"].get<std::vector<int>>();
 				if (!frameRatesList || (frameRatesList->size() != 1 &&
 							frameRatesList->size() != 2)) {
 					LOG(Virtual, Error) << "Invalid frame_rates: either one or two values";
diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
index a21e589fdd86..2b3723287051 100644
--- a/src/libcamera/yaml_parser.cpp
+++ b/src/libcamera/yaml_parser.cpp
@@ -109,12 +109,16 @@  std::size_t YamlObject::size() const
 
 /**
  * \fn template<typename T> YamlObject::get<T>() const
+ * \tparam T Type of the value
  * \brief Parse the YamlObject as a \a T value
  *
  * This function parses the value of the YamlObject as a \a T object, and
  * returns the value. If parsing fails (usually because the YamlObject doesn't
  * store a \a T value), std::nullopt is returned.
  *
+ * If the type \a T is an std::vector, the YamlObject will be parsed as a list
+ * of values.
+ *
  * \return The YamlObject value, or std::nullopt if parsing failed
  */
 
@@ -127,6 +131,9 @@  std::size_t YamlObject::size() const
  * returns the value. If parsing fails (usually because the YamlObject doesn't
  * store a \a T value), the \a defaultValue is returned.
  *
+ * Unlike the get() function, this overload does not support std::vector for the
+ * type \a T.
+ *
  * \return The YamlObject value, or \a defaultValue if parsing failed
  */
 
@@ -239,65 +246,50 @@  YamlObject::Accessor<Size>::get(const YamlObject &obj) const
 	return Size(*width, *height);
 }
 
-#endif /* __DOXYGEN__ */
-
-/**
- * \fn template<typename T> YamlObject::getList<T>() const
- * \brief Parse the YamlObject as a list of \a T
- *
- * This function parses the value of the YamlObject as a list of \a T objects,
- * and returns the value as a \a std::vector<T>. If parsing fails, std::nullopt
- * is returned.
- *
- * \return The YamlObject value as a std::vector<T>, or std::nullopt if parsing
- * failed
- */
-
-#ifndef __DOXYGEN__
-
-template<typename T,
-	 std::enable_if_t<
-		 std::is_same_v<bool, T> ||
-		 std::is_same_v<float, T> ||
-		 std::is_same_v<double, T> ||
-		 std::is_same_v<int8_t, T> ||
-		 std::is_same_v<uint8_t, T> ||
-		 std::is_same_v<int16_t, T> ||
-		 std::is_same_v<uint16_t, T> ||
-		 std::is_same_v<int32_t, T> ||
-		 std::is_same_v<uint32_t, T> ||
-		 std::is_same_v<std::string, T> ||
-		 std::is_same_v<Size, T>> *>
-std::optional<std::vector<T>> YamlObject::getList() const
+template<typename T>
+struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<
+	std::is_same_v<bool, T> ||
+	std::is_same_v<float, T> ||
+	std::is_same_v<double, T> ||
+	std::is_same_v<int8_t, T> ||
+	std::is_same_v<uint8_t, T> ||
+	std::is_same_v<int16_t, T> ||
+	std::is_same_v<uint16_t, T> ||
+	std::is_same_v<int32_t, T> ||
+	std::is_same_v<uint32_t, T> ||
+	std::is_same_v<std::string, T> ||
+	std::is_same_v<Size, T>>>
 {
-	if (type_ != Type::List)
-		return std::nullopt;
-
-	std::vector<T> values;
-	values.reserve(list_.size());
-
-	for (const YamlObject &entry : asList()) {
-		const auto value = entry.get<T>();
-		if (!value)
+	std::optional<std::vector<T>> get(const YamlObject &obj) const
+	{
+		if (obj.type_ != Type::List)
 			return std::nullopt;
-		values.emplace_back(*value);
+
+		std::vector<T> values;
+		values.reserve(obj.list_.size());
+
+		for (const YamlObject &entry : obj.asList()) {
+			const auto value = entry.get<T>();
+			if (!value)
+				return std::nullopt;
+			values.emplace_back(*value);
+		}
+
+		return values;
 	}
+};
 
-	return values;
-}
-
-template std::optional<std::vector<bool>> YamlObject::getList<bool>() const;
-template std::optional<std::vector<float>> YamlObject::getList<float>() const;
-template std::optional<std::vector<double>> YamlObject::getList<double>() const;
-template std::optional<std::vector<int8_t>> YamlObject::getList<int8_t>() const;
-template std::optional<std::vector<uint8_t>> YamlObject::getList<uint8_t>() const;
-template std::optional<std::vector<int16_t>> YamlObject::getList<int16_t>() const;
-template std::optional<std::vector<uint16_t>> YamlObject::getList<uint16_t>() const;
-template std::optional<std::vector<int32_t>> YamlObject::getList<int32_t>() const;
-template std::optional<std::vector<uint32_t>> YamlObject::getList<uint32_t>() const;
-template std::optional<std::vector<std::string>> YamlObject::getList<std::string>() const;
-template std::optional<std::vector<Size>> YamlObject::getList<Size>() const;
-
+template struct YamlObject::Accessor<std::vector<bool>>;
+template struct YamlObject::Accessor<std::vector<float>>;
+template struct YamlObject::Accessor<std::vector<double>>;
+template struct YamlObject::Accessor<std::vector<int8_t>>;
+template struct YamlObject::Accessor<std::vector<uint8_t>>;
+template struct YamlObject::Accessor<std::vector<int16_t>>;
+template struct YamlObject::Accessor<std::vector<uint16_t>>;
+template struct YamlObject::Accessor<std::vector<int32_t>>;
+template struct YamlObject::Accessor<std::vector<uint32_t>>;
+template struct YamlObject::Accessor<std::vector<std::string>>;
+template struct YamlObject::Accessor<std::vector<Size>>;
 #endif /* __DOXYGEN__ */
 
 /**
diff --git a/test/yaml-parser.cpp b/test/yaml-parser.cpp
index 1b22c87b72f1..566401afcab6 100644
--- a/test/yaml-parser.cpp
+++ b/test/yaml-parser.cpp
@@ -587,9 +587,9 @@  protected:
 			return TestFail;
 		}
 
-		const auto &values = firstElement.getList<uint16_t>();
+		const auto &values = firstElement.get<std::vector<uint16_t>>();
 		if (!values || values->size() != 2 || (*values)[0] != 1 || (*values)[1] != 2) {
-			cerr << "getList() failed to return correct vector" << std::endl;
+			cerr << "get() failed to return correct vector" << std::endl;
 			return TestFail;
 		}