Исходя из моего комментария, мне было интересно, может ли что-то подобное сработать:
enum class pci_format { domain_function, domain, function };
template <pci_format E> struct tag { };
class pci_location {
public:
pci_location (tag<pci_format::domain_function>, unsigned domain, unsigned bus,
unsigned device, unsigned function)
: format_(pci_format::domain_function)
, domain_(domain)
, bus_(bus)
, device_(device)
, function_(function)
{ }
// Repeat for other values of pci_format.
pci_format format () const { return format_; }
bool has_domain () const {
return (format_ == pci_format::domain_function)
or (format_ == pci_format::domain);
}
unsigned domain () const {
if (not has_domain()) { throw std::runtime_error("Domain not available."); }
return domain_;
}
// Repeat for other fields.
private:
pci_format format_;
unsigned domain_;
unsigned bus_;
unsigned device_;
unsigned function_
};
В основном вы создадите специфицированный c конструктор для каждого "формата" PCI. Конечно, вы также можете хранить каждый unsigned
как std::optional<unsigned>
, но это заставит пользователей «разыменовывать» каждый необязательный параметр, даже если они точно знали, что он должен содержать значение.
Так или иначе Им придется проверить, в каком «формате» находится местоположение, поэтому мне кажется, что использование enum для этого более удобно для пользователя. Тогда пользователям нужно только проверить один раз и точно знать, какие поля доступны.
Я полагаю, что вы могли бы поместить посетителя поверх всего этого, чтобы они могли просто предоставить код для выполнения для каждого "формата":
struct pci_location_visitor {
virtual void visit (tag<pci_format::domain_function>, pci_location const & obj) = 0;
// Repeat for other enum values.
};
// Add to pci_location:
void accept (pci_location_visitor & visitor) {
switch (format_) {
case pci_format::domain_function:
return visitor.visit(tag<pci_format::domain_function>{}, *this);
default: throw std::runtime_error("Format not supported for visitation.");
}
}
Затем вы можете создать посетителя, который может быть построен из набора вызываемых элементов, то есть лямбд, так что все это можно использовать, как показано ниже:
pci_location const & loc = getIt();
auto printSomething = make_pci_location_visitor(
[](tag<pci_format::domain_function>, pci_location const & e) { std::cout << e.domain(); }
, [](tag<pci_format::domain>, pci_location const & e) { std::cout << e.bus(); }
, [](tag<pci_format::function>, pci_location const & e) { std::cout << e.function(); }
);
loc.accept(printSomething);
Для пример того, как можно создать такого посетителя, см. класс overloaded
в примере std::visit
на cppreference.com .