Message ID | 20220510071617.42227-1-tomi.valkeinen@ideasonboard.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
On 10/05/2022 10:16, Tomi Valkeinen wrote: > This is just a conversation starter, not for merging. I really like > libfmt, and I really hate the C++ stream-based string formatting. libfmt > is the first library I add to any new C++ project I make. > > You can find more information about libfmt from: > > https://github.com/fmtlib/fmt > https://fmt.dev/latest/index.html > > This patch is just a crude conversion with ugly macros to showcase what > the formatting code might look like. As for the PR and EPR macros, those was just something to get forward with. I think fmt::print("") is fine, but fmt::print(stderr, "") is a bit long. Compared to cout, "fmt::print(" is actually shorter than "std::cout << ", and without all those << and std::endl the lines are shorter. Not so with cerr and fmt::print(stderr, (although in many cases the total length would still be shorter) but I think it makes sense to have a macro/inline func for error prints, if only to make it more obvious that it's an error print. And, of course, libfmt could also be used for logging: LOG(Camera, Error) << "Camera in " << camera_state_names[currentState] << " state trying " << from << "() requiring state " << camera_state_names[state]; to LOG(Camera, Error, "Camera in {} state trying {}() requiring state {}", camera_state_names[currentState], from, camera_state_names[state]); Tomi
Hi Tomi, On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel < libcamera-devel@lists.libcamera.org> wrote: > This is just a conversation starter, not for merging. I really like > libfmt, and I really hate the C++ stream-based string formatting. libfmt > is the first library I add to any new C++ project I make. > > You can find more information about libfmt from: > > https://github.com/fmtlib/fmt > https://fmt.dev/latest/index.html > > This patch is just a crude conversion with ugly macros to showcase what > the formatting code might look like. > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > than iostreams, but for the size it didn't seem to be true in cam's case > as the tests below show. However, simple prints did reduce the exe size, > but the few more complex ones increased the size. > > Size tests with gcc 11.2.0-19ubuntu1 > > - Without libfmt > > debug 3523400 > debug lto 3269368 > release 223056 > release lto 172280 > > - With libfmt > > debug 4424256 > debug lto 4143840 > release 303952 > release lto 252640 > > Above shows that cam's size clearly increases with libfmt. However, the > increase really comes only from one case, the use of fmt::memory_buffer > and std::back_inserter. Converting that code to use fmt::format() and > naive string append gives: > > release with string append 233680 > release lto with string append 186936 > > Also, if I add another use of fmt::memory_buffer and std::back_inserter > to another file, I see much less increase in the size: > > release lto with two uses of memory_buffer, back_inserter 256736 > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > For what it's worth, I absolutely loathe formatting in std iostream, and libfmt is a wonderful alternative. It also is close enough to the C++20 std::format implementation that eventual porting would be low effort. So I am all for this change :) Regards, Naush > --- > src/cam/camera_session.cpp | 105 ++++++++++++++++--------------------- > src/cam/drm.cpp | 68 ++++++++---------------- > src/cam/event_loop.cpp | 9 ++-- > src/cam/file_sink.cpp | 31 +++++------ > src/cam/image.cpp | 15 +++--- > src/cam/kms_sink.cpp | 38 ++++++-------- > src/cam/main.cpp | 28 +++++----- > src/cam/meson.build | 5 +- > src/cam/options.cpp | 66 ++++++++++------------- > src/cam/stream_options.cpp | 18 +++---- > 10 files changed, 165 insertions(+), 218 deletions(-) > > diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp > index efffafbf..7843c3fd 100644 > --- a/src/cam/camera_session.cpp > +++ b/src/cam/camera_session.cpp > @@ -5,10 +5,9 @@ > * camera_session.cpp - Camera capture session > */ > > -#include <iomanip> > -#include <iostream> > #include <limits.h> > -#include <sstream> > +#include <fmt/format.h> > +#include <fmt/ostream.h> > > #include <libcamera/control_ids.h> > #include <libcamera/property_ids.h> > @@ -22,6 +21,9 @@ > #include "main.h" > #include "stream_options.h" > > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > + > using namespace libcamera; > > CameraSession::CameraSession(CameraManager *cm, > @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm, > camera_ = cm->get(cameraId); > > if (!camera_) { > - std::cerr << "Camera " << cameraId << " not found" << > std::endl; > + EPR("Camera {} not found\n", cameraId); > return; > } > > if (camera_->acquire()) { > - std::cerr << "Failed to acquire camera " << cameraId > - << std::endl; > + EPR("Failed to acquire camera {}", cameraId); > return; > } > > @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm, > std::unique_ptr<CameraConfiguration> config = > camera_->generateConfiguration(roles); > if (!config || config->size() != roles.size()) { > - std::cerr << "Failed to get default stream configuration" > - << std::endl; > + EPR("Failed to get default stream configuration\n"); > return; > } > > /* Apply configuration if explicitly requested. */ > if (StreamKeyValueParser::updateConfiguration(config.get(), > > options_[OptStream])) { > - std::cerr << "Failed to update configuration" << std::endl; > + EPR("Failed to update configuration\n"); > return; > } > > @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm, > #ifdef HAVE_KMS > if (options_.isSet(OptDisplay)) { > if (options_.isSet(OptFile)) { > - std::cerr << "--display and --file options are > mutually exclusive" > - << std::endl; > + EPR("--display and --file options are mutually > exclusive\n"); > return; > } > > if (roles.size() != 1) { > - std::cerr << "Display doesn't support multiple > streams" > - << std::endl; > + EPR("Display doesn't support multiple streams\n"); > return; > } > > if (roles[0] != StreamRole::Viewfinder) { > - std::cerr << "Display requires a viewfinder stream" > - << std::endl; > + EPR("Display requires a viewfinder stream\n"); > return; > } > } > @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm, > > case CameraConfiguration::Adjusted: > if (strictFormats) { > - std::cout << "Adjusting camera configuration > disallowed by --strict-formats argument" > - << std::endl; > + PR("Adjusting camera configuration disallowed by > --strict-formats argument\n"); > return; > } > - std::cout << "Camera configuration adjusted" << std::endl; > + PR("Camera configuration adjusted\n"); > break; > > case CameraConfiguration::Invalid: > - std::cout << "Camera configuration invalid" << std::endl; > + PR("Camera configuration invalid\n"); > return; > } > > @@ -121,8 +117,7 @@ CameraSession::~CameraSession() > void CameraSession::listControls() const > { > for (const auto &[id, info] : camera_->controls()) { > - std::cout << "Control: " << id->name() << ": " > - << info.toString() << std::endl; > + PR("Control: {}: {}}n", id->name(), info.toString()); > } > } > > @@ -131,8 +126,7 @@ void CameraSession::listProperties() const > for (const auto &[key, value] : camera_->properties()) { > const ControlId *id = properties::properties.at(key); > > - std::cout << "Property: " << id->name() << " = " > - << value.toString() << std::endl; > + PR("Property: {} = {}\n", id->name(), value.toString()); > } > } > > @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const > { > unsigned int index = 0; > for (const StreamConfiguration &cfg : *config_) { > - std::cout << index << ": " << cfg.toString() << std::endl; > + PR("{}: {}\n", index, cfg.toString()); > > const StreamFormats &formats = cfg.formats(); > for (PixelFormat pixelformat : formats.pixelformats()) { > - std::cout << " * Pixelformat: " > - << pixelformat << " " > - << formats.range(pixelformat).toString() > - << std::endl; > + PR(" * Pixelformat: {} {}\n", pixelformat, > + formats.range(pixelformat).toString()); > > for (const Size &size : formats.sizes(pixelformat)) > - std::cout << " - " << size << std::endl; > + PR(" - {}\n", size); > } > > index++; > @@ -168,7 +160,7 @@ int CameraSession::start() > > ret = camera_->configure(config_.get()); > if (ret < 0) { > - std::cout << "Failed to configure camera" << std::endl; > + PR("Failed to configure camera\n"); > return ret; > } > > @@ -197,8 +189,7 @@ int CameraSession::start() > if (sink_) { > ret = sink_->configure(*config_); > if (ret < 0) { > - std::cout << "Failed to configure frame sink" > - << std::endl; > + PR("Failed to configure frame sink\n"); > return ret; > } > > @@ -214,12 +205,12 @@ void CameraSession::stop() > { > int ret = camera_->stop(); > if (ret) > - std::cout << "Failed to stop capture" << std::endl; > + PR("Failed to stop capture\n"); > > if (sink_) { > ret = sink_->stop(); > if (ret) > - std::cout << "Failed to stop frame sink" << > std::endl; > + PR("Failed to stop frame sink\n"); > } > > sink_.reset(); > @@ -238,7 +229,7 @@ int CameraSession::startCapture() > for (StreamConfiguration &cfg : *config_) { > ret = allocator_->allocate(cfg.stream()); > if (ret < 0) { > - std::cerr << "Can't allocate buffers" << std::endl; > + EPR("Can't allocate buffers\n"); > return -ENOMEM; > } > > @@ -254,7 +245,7 @@ int CameraSession::startCapture() > for (unsigned int i = 0; i < nbuffers; i++) { > std::unique_ptr<Request> request = > camera_->createRequest(); > if (!request) { > - std::cerr << "Can't create request" << std::endl; > + EPR("Can't create request\n"); > return -ENOMEM; > } > > @@ -266,8 +257,7 @@ int CameraSession::startCapture() > > ret = request->addBuffer(stream, buffer.get()); > if (ret < 0) { > - std::cerr << "Can't set buffer for request" > - << std::endl; > + EPR("Can't set buffer for request\n"); > return ret; > } > > @@ -281,14 +271,14 @@ int CameraSession::startCapture() > if (sink_) { > ret = sink_->start(); > if (ret) { > - std::cout << "Failed to start frame sink" << > std::endl; > + PR("Failed to start frame sink\n"); > return ret; > } > } > > ret = camera_->start(); > if (ret) { > - std::cout << "Failed to start capture" << std::endl; > + PR("Failed to start capture\n"); > if (sink_) > sink_->stop(); > return ret; > @@ -297,7 +287,7 @@ int CameraSession::startCapture() > for (std::unique_ptr<Request> &request : requests_) { > ret = queueRequest(request.get()); > if (ret < 0) { > - std::cerr << "Can't queue request" << std::endl; > + EPR("Can't queue request\n"); > camera_->stop(); > if (sink_) > sink_->stop(); > @@ -306,13 +296,11 @@ int CameraSession::startCapture() > } > > if (captureLimit_) > - std::cout << "cam" << cameraIndex_ > - << ": Capture " << captureLimit_ << " frames" > - << std::endl; > + PR("cam{}: Capture {} frames\n", cameraIndex_, > + captureLimit_); > else > - std::cout << "cam" << cameraIndex_ > - << ": Capture until user interrupts by SIGINT" > - << std::endl; > + PR("cam{}: Capture until user interrupts by SIGINT\n", > + cameraIndex_); > > return 0; > } > @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request) > > bool requeue = true; > > - std::stringstream info; > - info << ts / 1000000000 << "." > - << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 > - << " (" << std::fixed << std::setprecision(2) << fps << " > fps)"; > + auto sbuf = fmt::memory_buffer(); > + fmt::format_to(std::back_inserter(sbuf), "{}.{:06} ({:.2f} fps)", > + ts / 1000000000, > + ts / 1000 % 1000000, > + fps); > > for (const auto &[stream, buffer] : buffers) { > const FrameMetadata &metadata = buffer->metadata(); > > - info << " " << streamNames_[stream] > - << " seq: " << std::setw(6) << std::setfill('0') << > metadata.sequence > - << " bytesused: "; > + fmt::format_to(std::back_inserter(sbuf), " {} seq: {:06} > bytesused: ", > + streamNames_[stream], metadata.sequence); > > unsigned int nplane = 0; > for (const FrameMetadata::Plane &plane : > metadata.planes()) { > - info << plane.bytesused; > + fmt::format_to(std::back_inserter(sbuf), "{}", > plane.bytesused); > if (++nplane < metadata.planes().size()) > - info << "/"; > + fmt::format_to(std::back_inserter(sbuf), > "/"); > } > } > > @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request) > requeue = false; > } > > - std::cout << info.str() << std::endl; > + PR("{}\n", fmt::to_string(sbuf)); > > if (printMetadata_) { > const ControlList &requestMetadata = request->metadata(); > for (const auto &[key, value] : requestMetadata) { > const ControlId *id = controls::controls.at(key); > - std::cout << "\t" << id->name() << " = " > - << value.toString() << std::endl; > + PR("\t{} = {}\n", id->name(), value.toString()); > } > } > > diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp > index 46e34eb5..84919ab3 100644 > --- a/src/cam/drm.cpp > +++ b/src/cam/drm.cpp > @@ -10,12 +10,12 @@ > #include <algorithm> > #include <errno.h> > #include <fcntl.h> > -#include <iostream> > #include <set> > #include <string.h> > #include <sys/ioctl.h> > #include <sys/stat.h> > #include <sys/types.h> > +#include <fmt/core.h> > > #include <libcamera/framebuffer.h> > #include <libcamera/geometry.h> > @@ -25,6 +25,9 @@ > > #include "event_loop.h" > > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > + > namespace DRM { > > Object::Object(Device *dev, uint32_t id, Type type) > @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const > drmModeConnector *connector) > { > auto typeName = connectorTypeNames.find(connector->connector_type); > if (typeName == connectorTypeNames.end()) { > - std::cerr > - << "Invalid connector type " > - << connector->connector_type << std::endl; > + EPR("Invalid connector type {}}n", > connector->connector_type); > typeName = > connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown); > } > > @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const > drmModeConnector *connector) > return e.id() == > encoderId; > }); > if (encoder == encoders.end()) { > - std::cerr > - << "Encoder " << encoderId << " not found" > - << std::endl; > + EPR("Encoder {} not found\n", encoderId); > continue; > } > > @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer() > > if (ret == -1) { > ret = -errno; > - std::cerr > - << "Failed to close GEM object: " > - << strerror(-ret) << std::endl; > + EPR("Failed to close GEM object: {}\n", > strerror(-ret)); > } > } > > @@ -408,9 +405,8 @@ int Device::init() > fd_ = open(name, O_RDWR | O_CLOEXEC); > if (fd_ < 0) { > ret = -errno; > - std::cerr > - << "Failed to open DRM/KMS device " << name << ": " > - << strerror(-ret) << std::endl; > + EPR("Failed to open DRM/KMS device {}: {}\n", name, > + strerror(-ret)); > return ret; > } > > @@ -421,9 +417,7 @@ int Device::init() > ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1); > if (ret < 0) { > ret = -errno; > - std::cerr > - << "Failed to enable atomic capability: " > - << strerror(-ret) << std::endl; > + EPR("Failed to enable atomic capability: {}\n", > strerror(-ret)); > return ret; > } > > @@ -448,9 +442,7 @@ int Device::getResources() > }; > if (!resources) { > ret = -errno; > - std::cerr > - << "Failed to get DRM/KMS resources: " > - << strerror(-ret) << std::endl; > + EPR("Failed to get DRM/KMS resources: {}\n", > strerror(-ret)); > return ret; > } > > @@ -458,9 +450,7 @@ int Device::getResources() > drmModeCrtc *crtc = drmModeGetCrtc(fd_, > resources->crtcs[i]); > if (!crtc) { > ret = -errno; > - std::cerr > - << "Failed to get CRTC: " << strerror(-ret) > - << std::endl; > + EPR("Failed to get CRTC: {}\n", strerror(-ret)); > return ret; > } > > @@ -476,9 +466,7 @@ int Device::getResources() > drmModeGetEncoder(fd_, resources->encoders[i]); > if (!encoder) { > ret = -errno; > - std::cerr > - << "Failed to get encoder: " << > strerror(-ret) > - << std::endl; > + EPR("Failed to get encoder: {}\n", strerror(-ret)); > return ret; > } > > @@ -494,9 +482,7 @@ int Device::getResources() > drmModeGetConnector(fd_, resources->connectors[i]); > if (!connector) { > ret = -errno; > - std::cerr > - << "Failed to get connector: " << > strerror(-ret) > - << std::endl; > + EPR("Failed to get connector: {}\n", > strerror(-ret)); > return ret; > } > > @@ -513,9 +499,7 @@ int Device::getResources() > }; > if (!planes) { > ret = -errno; > - std::cerr > - << "Failed to get DRM/KMS planes: " > - << strerror(-ret) << std::endl; > + EPR("Failed to get DRM/KMS planes: {}\n", strerror(-ret)); > return ret; > } > > @@ -524,9 +508,7 @@ int Device::getResources() > drmModeGetPlane(fd_, planes->planes[i]); > if (!plane) { > ret = -errno; > - std::cerr > - << "Failed to get plane: " << > strerror(-ret) > - << std::endl; > + EPR("Failed to get plane: {}\n", strerror(-ret)); > return ret; > } > > @@ -556,9 +538,7 @@ int Device::getResources() > drmModePropertyRes *property = drmModeGetProperty(fd_, id); > if (!property) { > ret = -errno; > - std::cerr > - << "Failed to get property: " << > strerror(-ret) > - << std::endl; > + EPR("Failed to get property: {}\n", > strerror(-ret)); > continue; > } > > @@ -573,9 +553,8 @@ int Device::getResources() > for (auto &object : objects_) { > ret = object.second->setup(); > if (ret < 0) { > - std::cerr > - << "Failed to setup object " << > object.second->id() > - << ": " << strerror(-ret) << std::endl; > + EPR("Failed to setup object {}: {}\n", > + object.second->id(), strerror(-ret)); > return ret; > } > } > @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( > ret = drmPrimeFDToHandle(fd_, plane.fd.get(), > &handle); > if (ret < 0) { > ret = -errno; > - std::cerr > - << "Unable to import framebuffer > dmabuf: " > - << strerror(-ret) << std::endl; > + EPR("Unable to import framebuffer dmabuf: > {}\n", > + strerror(-ret)); > return nullptr; > } > > @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( > strides.data(), offsets, &fb->id_, 0); > if (ret < 0) { > ret = -errno; > - std::cerr > - << "Failed to add framebuffer: " > - << strerror(-ret) << std::endl; > + EPR("Failed to add framebuffer: {}\n", strerror(-ret)); > return nullptr; > } > > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp > index e25784c0..87aaf59a 100644 > --- a/src/cam/event_loop.cpp > +++ b/src/cam/event_loop.cpp > @@ -10,7 +10,10 @@ > #include <assert.h> > #include <event2/event.h> > #include <event2/thread.h> > -#include <iostream> > +#include <fmt/core.h> > + > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > EventLoop *EventLoop::instance_ = nullptr; > > @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type, > event->event_ = event_new(base_, fd, events, > &EventLoop::Event::dispatch, > event.get()); > if (!event->event_) { > - std::cerr << "Failed to create event for fd " << fd << > std::endl; > + EPR("Failed to create event for fd {}\n", fd); > return; > } > > int ret = event_add(event->event_, nullptr); > if (ret < 0) { > - std::cerr << "Failed to add event for fd " << fd << > std::endl; > + EPR("Failed to add event for fd {}\n", fd); > return; > } > > diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp > index 45213d4a..86e2118c 100644 > --- a/src/cam/file_sink.cpp > +++ b/src/cam/file_sink.cpp > @@ -7,11 +7,12 @@ > > #include <assert.h> > #include <fcntl.h> > -#include <iomanip> > -#include <iostream> > -#include <sstream> > #include <string.h> > #include <unistd.h> > +#include <fmt/core.h> > + > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > #include <libcamera/camera.h> > > @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream, > FrameBuffer *buffer) > > pos = filename.find_first_of('#'); > if (pos != std::string::npos) { > - std::stringstream ss; > - ss << streamNames_[stream] << "-" << std::setw(6) > - << std::setfill('0') << buffer->metadata().sequence; > - filename.replace(pos, 1, ss.str()); > + auto s = fmt::format("{}-{:06}", > + streamNames_[stream], > + buffer->metadata().sequence); > + filename.replace(pos, 1, s); > } > > fd = open(filename.c_str(), O_CREAT | O_WRONLY | > @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream, > FrameBuffer *buffer) > S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | > S_IWOTH); > if (fd == -1) { > ret = -errno; > - std::cerr << "failed to open file " << filename << ": " > - << strerror(-ret) << std::endl; > + EPR("failed to open file {}: {}\n", filename, > strerror(-ret)); > return; > } > > @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream, > FrameBuffer *buffer) > unsigned int length = std::min<unsigned > int>(meta.bytesused, data.size()); > > if (meta.bytesused > data.size()) > - std::cerr << "payload size " << meta.bytesused > - << " larger than plane size " << > data.size() > - << std::endl; > + EPR("payload size {} larger than plane size {}\n", > + meta.bytesused, data.size()); > > ret = ::write(fd, data.data(), length); > if (ret < 0) { > ret = -errno; > - std::cerr << "write error: " << strerror(-ret) > - << std::endl; > + EPR("write error: {}\n", strerror(-ret)); > break; > } else if (ret != (int)length) { > - std::cerr << "write error: only " << ret > - << " bytes written instead of " > - << length << std::endl; > + EPR("write error: only {} bytes written instead of > {}\n", > + ret, length); > break; > } > } > diff --git a/src/cam/image.cpp b/src/cam/image.cpp > index fe2cc6da..73bcf915 100644 > --- a/src/cam/image.cpp > +++ b/src/cam/image.cpp > @@ -9,11 +9,14 @@ > > #include <assert.h> > #include <errno.h> > -#include <iostream> > #include <map> > #include <string.h> > #include <sys/mman.h> > #include <unistd.h> > +#include <fmt/core.h> > + > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > using namespace libcamera; > > @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const > FrameBuffer *buffer, MapMode > > if (plane.offset > length || > plane.offset + plane.length > length) { > - std::cerr << "plane is out of buffer: buffer > length=" > - << length << ", plane offset=" << > plane.offset > - << ", plane length=" << plane.length > - << std::endl; > + EPR("plane is out of buffer: buffer length={}, > plane offset={}, plane length={}\n", > + length, plane.offset, plane.length); > return nullptr; > } > size_t &mapLength = mappedBuffers[fd].mapLength; > @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const > FrameBuffer *buffer, MapMode > MAP_SHARED, fd, 0); > if (address == MAP_FAILED) { > int error = -errno; > - std::cerr << "Failed to mmap plane: " > - << strerror(-error) << std::endl; > + EPR("Failed to mmap plane: {}\n", > + strerror(-error)); > return nullptr; > } > > diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp > index 7add81a6..823b75e4 100644 > --- a/src/cam/kms_sink.cpp > +++ b/src/cam/kms_sink.cpp > @@ -10,10 +10,13 @@ > #include <array> > #include <algorithm> > #include <assert.h> > -#include <iostream> > #include <memory> > #include <stdint.h> > #include <string.h> > +#include <fmt/core.h> > + > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > #include <libcamera/camera.h> > #include <libcamera/formats.h> > @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName) > > if (!connector_) { > if (!connectorName.empty()) > - std::cerr > - << "Connector " << connectorName << " not > found" > - << std::endl; > + EPR("Connector {} not found\n", connectorName); > else > - std::cerr << "No connected connector found" << > std::endl; > + EPR("No connected connector found\n"); > return; > } > > @@ -119,7 +120,7 @@ int KMSSink::configure(const > libcamera::CameraConfiguration &config) > mode.vdisplay == > cfg.size.height; > }); > if (iter == modes.end()) { > - std::cerr << "No mode matching " << cfg.size << std::endl; > + EPR("No mode matching {}\n", cfg.size); > return -EINVAL; > } > > @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const > libcamera::PixelFormat &format) > { > const int ret = selectPipeline(format); > if (ret) { > - std::cerr > - << "Unable to find display pipeline for format " > - << format << std::endl; > - > + EPR("Unable to find display pipeline for format {}\n", > format); > return ret; > } > > - std::cout > - << "Using KMS plane " << plane_->id() << ", CRTC " << > crtc_->id() > - << ", connector " << connector_->name() > - << " (" << connector_->id() << ")" << std::endl; > + PR("Using KMS plane {}, CRTC {}, connector {} ({})\n", > + plane_->id(), crtc_->id(), connector_->name(), > connector_->id()); > > return 0; > } > @@ -228,9 +224,8 @@ int KMSSink::start() > > ret = request->commit(DRM::AtomicRequest::FlagAllowModeset); > if (ret < 0) { > - std::cerr > - << "Failed to disable CRTCs and planes: " > - << strerror(-ret) << std::endl; > + EPR("Failed to disable CRTCs and planes: {}\n", > + strerror(-ret)); > return ret; > } > > @@ -250,9 +245,7 @@ int KMSSink::stop() > > int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset); > if (ret < 0) { > - std::cerr > - << "Failed to stop display pipeline: " > - << strerror(-ret) << std::endl; > + EPR("Failed to stop display pipeline: {}\n", > strerror(-ret)); > return ret; > } > > @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request > *camRequest) > if (!queued_) { > int ret = drmRequest->commit(flags); > if (ret < 0) { > - std::cerr > - << "Failed to commit atomic request: " > - << strerror(-ret) << std::endl; > + EPR("Failed to commit atomic request: {}\n", > + strerror(-ret)); > /* \todo Implement error handling */ > } > > diff --git a/src/cam/main.cpp b/src/cam/main.cpp > index c7f664b9..03615dc9 100644 > --- a/src/cam/main.cpp > +++ b/src/cam/main.cpp > @@ -6,10 +6,9 @@ > */ > > #include <atomic> > -#include <iomanip> > -#include <iostream> > #include <signal.h> > #include <string.h> > +#include <fmt/core.h> > > #include <libcamera/libcamera.h> > #include <libcamera/property_ids.h> > @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv) > > ret = cm_->start(); > if (ret) { > - std::cout << "Failed to start camera manager: " > - << strerror(-ret) << std::endl; > + fmt::print("Failed to start camera manager: {}\n", -ret); > return ret; > } > > @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[]) > > void CamApp::cameraAdded(std::shared_ptr<Camera> cam) > { > - std::cout << "Camera Added: " << cam->id() << std::endl; > + fmt::print("Camera Added: {}\n", cam->id()); > } > > void CamApp::cameraRemoved(std::shared_ptr<Camera> cam) > { > - std::cout << "Camera Removed: " << cam->id() << std::endl; > + fmt::print("Camera Removed: {}\n", cam->id()); > } > > void CamApp::captureDone() > @@ -193,11 +191,11 @@ int CamApp::run() > > /* 1. List all cameras. */ > if (options_.isSet(OptList)) { > - std::cout << "Available cameras:" << std::endl; > + fmt::print("Available cameras:\n"); > > unsigned int index = 1; > for (const std::shared_ptr<Camera> &cam : cm_->cameras()) { > - std::cout << index << ": " << > cameraName(cam.get()) << std::endl; > + fmt::print("{}: {}\n", cameraName(cam.get()), > index); > index++; > } > } > @@ -215,12 +213,12 @@ int CamApp::run() > index, > > camera.children()); > if (!session->isValid()) { > - std::cout << "Failed to create camera > session" << std::endl; > + fmt::print("Failed to create camera > session\n"); > return -EINVAL; > } > > - std::cout << "Using camera " << > session->camera()->id() > - << " as cam" << index << std::endl; > + fmt::print("Using camera{} as cam{}\n", > + session->camera()->id(), index); > > session->captureDone.connect(this, > &CamApp::captureDone); > > @@ -250,7 +248,7 @@ int CamApp::run() > > ret = session->start(); > if (ret) { > - std::cout << "Failed to start camera session" << > std::endl; > + fmt::print("Failed to start camera session\n"); > return ret; > } > > @@ -259,8 +257,8 @@ int CamApp::run() > > /* 5. Enable hotplug monitoring. */ > if (options_.isSet(OptMonitor)) { > - std::cout << "Monitoring new hotplug and unplug events" << > std::endl; > - std::cout << "Press Ctrl-C to interrupt" << std::endl; > + fmt::print("Monitoring new hotplug and unplug events\n"); > + fmt::print("Press Ctrl-C to interrupt\n"); > > cm_->cameraAdded.connect(this, &CamApp::cameraAdded); > cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved); > @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera) > > void signalHandler([[maybe_unused]] int signal) > { > - std::cout << "Exiting" << std::endl; > + fmt::print("Exiting"); > CamApp::instance()->quit(); > } > > diff --git a/src/cam/meson.build b/src/cam/meson.build > index 5bab8c9e..2b47383d 100644 > --- a/src/cam/meson.build > +++ b/src/cam/meson.build > @@ -7,6 +7,8 @@ if not libevent.found() > subdir_done() > endif > > +libfmt_dep = dependency('fmt') > + > cam_enabled = true > > cam_sources = files([ > @@ -25,7 +27,7 @@ cam_cpp_args = [] > libdrm = dependency('libdrm', required : false) > > if libdrm.found() > - cam_cpp_args += [ '-DHAVE_KMS' ] > + cam_cpp_args += [ '-DHAVE_KMS', ] > cam_sources += files([ > 'drm.cpp', > 'kms_sink.cpp' > @@ -38,6 +40,7 @@ cam = executable('cam', cam_sources, > libcamera_public, > libdrm, > libevent, > + libfmt_dep, > ], > cpp_args : cam_cpp_args, > install : true) > diff --git a/src/cam/options.cpp b/src/cam/options.cpp > index 4f7e8691..c9979385 100644 > --- a/src/cam/options.cpp > +++ b/src/cam/options.cpp > @@ -7,9 +7,11 @@ > > #include <assert.h> > #include <getopt.h> > -#include <iomanip> > -#include <iostream> > #include <string.h> > +#include <fmt/core.h> > + > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > #include "options.h" > > @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const > char *arguments) > continue; > > if (optionsMap_.find(key) == optionsMap_.end()) { > - std::cerr << "Invalid option " << key << std::endl; > + EPR("Invalid option {}\n", key); > return options; > } > > OptionArgument arg = optionsMap_[key].argument; > if (value.empty() && arg == ArgumentRequired) { > - std::cerr << "Option " << key << " requires an > argument" > - << std::endl; > + EPR("Option {} requires an argument\n", key); > return options; > } else if (!value.empty() && arg == ArgumentNone) { > - std::cerr << "Option " << key << " takes no > argument" > - << std::endl; > + EPR("Option {} takes no argument\n", key); > return options; > } > > const Option &option = optionsMap_[key]; > if (!options.parseValue(key, option, value.c_str())) { > - std::cerr << "Failed to parse '" << value << "' as > " > - << option.typeName() << " for option " > << key > - << std::endl; > + EPR("Failed to parse '{}' as {} for option {}\n", > + value, option.typeName(), key); > return options; > } > } > @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent) > argument += "]"; > } > > - std::cerr << std::setw(indent) << argument; > + EPR("{:{}}", argument, indent); > > for (const char *help = option.help, *end = help; end;) { > end = strchr(help, '\n'); > if (end) { > - std::cerr << std::string(help, end - help > + 1); > - std::cerr << std::setw(indent) << " "; > + EPR(std::string(help, end - help + 1)); > + EPR("{:{}}", "", indent); > help = end + 1; > } else { > - std::cerr << help << std::endl; > + EPR("{}\n", help); > } > } > } > @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int > argc, char **argv) > > if (c == '?' || c == ':') { > if (c == '?') > - std::cerr << "Invalid option "; > + EPR("Invalid option "); > else > - std::cerr << "Missing argument for option > "; > - std::cerr << argv[optind - 1] << std::endl; > + EPR("Missing argument for option "); > + EPR("{}\n", argv[optind - 1]); > > usage(); > return options; > @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc, > char **argv) > } > > if (optind < argc) { > - std::cerr << "Invalid non-option argument '" << > argv[optind] > - << "'" << std::endl; > + EPR("Invalid non-option argument '{}'\n", argv[optind]); > usage(); > return options; > } > @@ -992,14 +990,9 @@ void OptionsParser::usage() > > indent = (indent + 7) / 8 * 8; > > - std::cerr << "Options:" << std::endl; > - > - std::ios_base::fmtflags f(std::cerr.flags()); > - std::cerr << std::left; > + EPR("Options:\n"); > > usageOptions(options_, indent); > - > - std::cerr.flags(f); > } > > void OptionsParser::usageOptions(const std::list<Option> &options, > @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const > std::list<Option> &options, > if (option.isArray) > argument += " ..."; > > - std::cerr << std::setw(indent) << argument; > + EPR("{:{}}", argument, indent); > > - for (const char *help = option.help, *end = help; end; ) { > + for (const char *help = option.help, *end = help; end;) { > end = strchr(help, '\n'); > if (end) { > - std::cerr << std::string(help, end - help > + 1); > - std::cerr << std::setw(indent) << " "; > + EPR(std::string(help, end - help + 1)); > + EPR("{:{}}", "", indent); > help = end + 1; > } else { > - std::cerr << help << std::endl; > + EPR("{}\n", help); > } > } > > @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const > std::list<Option> &options, > return; > > for (const Option *option : parentOptions) { > - std::cerr << std::endl << "Options valid in the context of > " > - << option->optionName() << ":" << std::endl; > + EPR("\nOptions valid in the context of {}:\n", > + option->optionName()); > usageOptions(option->children, indent); > } > } > @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option > &option, const char *arg, > > std::tie(options, error) = childOption(option.parent, options); > if (error) { > - std::cerr << "Option " << option.optionName() << " > requires a " > - << error->optionName() << " context" << > std::endl; > + EPR("Option {} requires a {} context\n", > + option.optionName(), error->optionName()); > return false; > } > > if (!options->parseValue(option.opt, option, arg)) { > - std::cerr << "Can't parse " << option.typeName() > - << " argument for option " << option.optionName() > - << std::endl; > + EPR("Can't parse {} argument for option {}\n", > + option.typeName(), option.optionName()); > return false; > } > > diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp > index 150bd27c..666862eb 100644 > --- a/src/cam/stream_options.cpp > +++ b/src/cam/stream_options.cpp > @@ -6,7 +6,10 @@ > */ > #include "stream_options.h" > > -#include <iostream> > +#include <fmt/core.h> > + > +#define PR(...) fmt::print(__VA_ARGS__) > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > using namespace libcamera; > > @@ -30,8 +33,7 @@ KeyValueParser::Options > StreamKeyValueParser::parse(const char *arguments) > > if (options.valid() && options.isSet("role") && > !parseRole(&role, options)) { > - std::cerr << "Unknown stream role " > - << options["role"].toString() << std::endl; > + EPR("Unknown stream role {}\n", > options["role"].toString()); > options.invalidate(); > } > > @@ -64,7 +66,7 @@ int > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, > const OptionValue &values) > { > if (!config) { > - std::cerr << "No configuration provided" << std::endl; > + EPR("No configuration provided\n"); > return -EINVAL; > } > > @@ -75,12 +77,8 @@ int > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, > const std::vector<OptionValue> &streamParameters = > values.toArray(); > > if (config->size() != streamParameters.size()) { > - std::cerr > - << "Number of streams in configuration " > - << config->size() > - << " does not match number of streams parsed " > - << streamParameters.size() > - << std::endl; > + EPR("Number of streams in configuration {} does not match > number of streams parsed {}\n", > + config->size(), streamParameters.size()); > return -EINVAL; > } > > -- > 2.34.1 > >
Quoting Naushir Patuck (2022-05-10 10:49:14) > Hi Tomi, > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel < > libcamera-devel@lists.libcamera.org> wrote: > > > This is just a conversation starter, not for merging. I really like > > libfmt, and I really hate the C++ stream-based string formatting. libfmt > > is the first library I add to any new C++ project I make. > > > > You can find more information about libfmt from: > > > > https://github.com/fmtlib/fmt > > https://fmt.dev/latest/index.html > > > > This patch is just a crude conversion with ugly macros to showcase what > > the formatting code might look like. > > > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > > than iostreams, but for the size it didn't seem to be true in cam's case > > as the tests below show. However, simple prints did reduce the exe size, > > but the few more complex ones increased the size. > > > > Size tests with gcc 11.2.0-19ubuntu1 > > > > - Without libfmt > > > > debug 3523400 > > debug lto 3269368 > > release 223056 > > release lto 172280 > > > > - With libfmt > > > > debug 4424256 > > debug lto 4143840 > > release 303952 > > release lto 252640 > > > > Above shows that cam's size clearly increases with libfmt. However, the > > increase really comes only from one case, the use of fmt::memory_buffer > > and std::back_inserter. Converting that code to use fmt::format() and > > naive string append gives: > > > > release with string append 233680 > > release lto with string append 186936 > > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter > > to another file, I see much less increase in the size: > > > > release lto with two uses of memory_buffer, back_inserter 256736 > > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > > > > For what it's worth, I absolutely loathe formatting in std iostream, and > libfmt is a wonderful alternative. > It also is close enough to the C++20 std::format implementation that > eventual porting would be low effort. So I am all for this change :) I've never used (yet) {fmt}, but I've only heard good things about performance, and of course it's headed into the standard, so I also think there is some good merit to be found in this development. -- Kieran > > Regards, > Naush > > > > > --- > > src/cam/camera_session.cpp | 105 ++++++++++++++++--------------------- > > src/cam/drm.cpp | 68 ++++++++---------------- > > src/cam/event_loop.cpp | 9 ++-- > > src/cam/file_sink.cpp | 31 +++++------ > > src/cam/image.cpp | 15 +++--- > > src/cam/kms_sink.cpp | 38 ++++++-------- > > src/cam/main.cpp | 28 +++++----- > > src/cam/meson.build | 5 +- > > src/cam/options.cpp | 66 ++++++++++------------- > > src/cam/stream_options.cpp | 18 +++---- > > 10 files changed, 165 insertions(+), 218 deletions(-) > > > > diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp > > index efffafbf..7843c3fd 100644 > > --- a/src/cam/camera_session.cpp > > +++ b/src/cam/camera_session.cpp > > @@ -5,10 +5,9 @@ > > * camera_session.cpp - Camera capture session > > */ > > > > -#include <iomanip> > > -#include <iostream> > > #include <limits.h> > > -#include <sstream> > > +#include <fmt/format.h> > > +#include <fmt/ostream.h> > > > > #include <libcamera/control_ids.h> > > #include <libcamera/property_ids.h> > > @@ -22,6 +21,9 @@ > > #include "main.h" > > #include "stream_options.h" > > > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > + > > using namespace libcamera; > > > > CameraSession::CameraSession(CameraManager *cm, > > @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm, > > camera_ = cm->get(cameraId); > > > > if (!camera_) { > > - std::cerr << "Camera " << cameraId << " not found" << > > std::endl; > > + EPR("Camera {} not found\n", cameraId); > > return; > > } > > > > if (camera_->acquire()) { > > - std::cerr << "Failed to acquire camera " << cameraId > > - << std::endl; > > + EPR("Failed to acquire camera {}", cameraId); > > return; > > } > > > > @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm, > > std::unique_ptr<CameraConfiguration> config = > > camera_->generateConfiguration(roles); > > if (!config || config->size() != roles.size()) { > > - std::cerr << "Failed to get default stream configuration" > > - << std::endl; > > + EPR("Failed to get default stream configuration\n"); > > return; > > } > > > > /* Apply configuration if explicitly requested. */ > > if (StreamKeyValueParser::updateConfiguration(config.get(), > > > > options_[OptStream])) { > > - std::cerr << "Failed to update configuration" << std::endl; > > + EPR("Failed to update configuration\n"); > > return; > > } > > > > @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm, > > #ifdef HAVE_KMS > > if (options_.isSet(OptDisplay)) { > > if (options_.isSet(OptFile)) { > > - std::cerr << "--display and --file options are > > mutually exclusive" > > - << std::endl; > > + EPR("--display and --file options are mutually > > exclusive\n"); > > return; > > } > > > > if (roles.size() != 1) { > > - std::cerr << "Display doesn't support multiple > > streams" > > - << std::endl; > > + EPR("Display doesn't support multiple streams\n"); > > return; > > } > > > > if (roles[0] != StreamRole::Viewfinder) { > > - std::cerr << "Display requires a viewfinder stream" > > - << std::endl; > > + EPR("Display requires a viewfinder stream\n"); > > return; > > } > > } > > @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm, > > > > case CameraConfiguration::Adjusted: > > if (strictFormats) { > > - std::cout << "Adjusting camera configuration > > disallowed by --strict-formats argument" > > - << std::endl; > > + PR("Adjusting camera configuration disallowed by > > --strict-formats argument\n"); > > return; > > } > > - std::cout << "Camera configuration adjusted" << std::endl; > > + PR("Camera configuration adjusted\n"); > > break; > > > > case CameraConfiguration::Invalid: > > - std::cout << "Camera configuration invalid" << std::endl; > > + PR("Camera configuration invalid\n"); > > return; > > } > > > > @@ -121,8 +117,7 @@ CameraSession::~CameraSession() > > void CameraSession::listControls() const > > { > > for (const auto &[id, info] : camera_->controls()) { > > - std::cout << "Control: " << id->name() << ": " > > - << info.toString() << std::endl; > > + PR("Control: {}: {}}n", id->name(), info.toString()); > > } > > } > > > > @@ -131,8 +126,7 @@ void CameraSession::listProperties() const > > for (const auto &[key, value] : camera_->properties()) { > > const ControlId *id = properties::properties.at(key); > > > > - std::cout << "Property: " << id->name() << " = " > > - << value.toString() << std::endl; > > + PR("Property: {} = {}\n", id->name(), value.toString()); > > } > > } > > > > @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const > > { > > unsigned int index = 0; > > for (const StreamConfiguration &cfg : *config_) { > > - std::cout << index << ": " << cfg.toString() << std::endl; > > + PR("{}: {}\n", index, cfg.toString()); > > > > const StreamFormats &formats = cfg.formats(); > > for (PixelFormat pixelformat : formats.pixelformats()) { > > - std::cout << " * Pixelformat: " > > - << pixelformat << " " > > - << formats.range(pixelformat).toString() > > - << std::endl; > > + PR(" * Pixelformat: {} {}\n", pixelformat, > > + formats.range(pixelformat).toString()); > > > > for (const Size &size : formats.sizes(pixelformat)) > > - std::cout << " - " << size << std::endl; > > + PR(" - {}\n", size); > > } > > > > index++; > > @@ -168,7 +160,7 @@ int CameraSession::start() > > > > ret = camera_->configure(config_.get()); > > if (ret < 0) { > > - std::cout << "Failed to configure camera" << std::endl; > > + PR("Failed to configure camera\n"); > > return ret; > > } > > > > @@ -197,8 +189,7 @@ int CameraSession::start() > > if (sink_) { > > ret = sink_->configure(*config_); > > if (ret < 0) { > > - std::cout << "Failed to configure frame sink" > > - << std::endl; > > + PR("Failed to configure frame sink\n"); > > return ret; > > } > > > > @@ -214,12 +205,12 @@ void CameraSession::stop() > > { > > int ret = camera_->stop(); > > if (ret) > > - std::cout << "Failed to stop capture" << std::endl; > > + PR("Failed to stop capture\n"); > > > > if (sink_) { > > ret = sink_->stop(); > > if (ret) > > - std::cout << "Failed to stop frame sink" << > > std::endl; > > + PR("Failed to stop frame sink\n"); > > } > > > > sink_.reset(); > > @@ -238,7 +229,7 @@ int CameraSession::startCapture() > > for (StreamConfiguration &cfg : *config_) { > > ret = allocator_->allocate(cfg.stream()); > > if (ret < 0) { > > - std::cerr << "Can't allocate buffers" << std::endl; > > + EPR("Can't allocate buffers\n"); > > return -ENOMEM; > > } > > > > @@ -254,7 +245,7 @@ int CameraSession::startCapture() > > for (unsigned int i = 0; i < nbuffers; i++) { > > std::unique_ptr<Request> request = > > camera_->createRequest(); > > if (!request) { > > - std::cerr << "Can't create request" << std::endl; > > + EPR("Can't create request\n"); > > return -ENOMEM; > > } > > > > @@ -266,8 +257,7 @@ int CameraSession::startCapture() > > > > ret = request->addBuffer(stream, buffer.get()); > > if (ret < 0) { > > - std::cerr << "Can't set buffer for request" > > - << std::endl; > > + EPR("Can't set buffer for request\n"); > > return ret; > > } > > > > @@ -281,14 +271,14 @@ int CameraSession::startCapture() > > if (sink_) { > > ret = sink_->start(); > > if (ret) { > > - std::cout << "Failed to start frame sink" << > > std::endl; > > + PR("Failed to start frame sink\n"); > > return ret; > > } > > } > > > > ret = camera_->start(); > > if (ret) { > > - std::cout << "Failed to start capture" << std::endl; > > + PR("Failed to start capture\n"); > > if (sink_) > > sink_->stop(); > > return ret; > > @@ -297,7 +287,7 @@ int CameraSession::startCapture() > > for (std::unique_ptr<Request> &request : requests_) { > > ret = queueRequest(request.get()); > > if (ret < 0) { > > - std::cerr << "Can't queue request" << std::endl; > > + EPR("Can't queue request\n"); > > camera_->stop(); > > if (sink_) > > sink_->stop(); > > @@ -306,13 +296,11 @@ int CameraSession::startCapture() > > } > > > > if (captureLimit_) > > - std::cout << "cam" << cameraIndex_ > > - << ": Capture " << captureLimit_ << " frames" > > - << std::endl; > > + PR("cam{}: Capture {} frames\n", cameraIndex_, > > + captureLimit_); > > else > > - std::cout << "cam" << cameraIndex_ > > - << ": Capture until user interrupts by SIGINT" > > - << std::endl; > > + PR("cam{}: Capture until user interrupts by SIGINT\n", > > + cameraIndex_); > > > > return 0; > > } > > @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request) > > > > bool requeue = true; > > > > - std::stringstream info; > > - info << ts / 1000000000 << "." > > - << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 > > - << " (" << std::fixed << std::setprecision(2) << fps << " > > fps)"; > > + auto sbuf = fmt::memory_buffer(); > > + fmt::format_to(std::back_inserter(sbuf), "{}.{:06} ({:.2f} fps)", > > + ts / 1000000000, > > + ts / 1000 % 1000000, > > + fps); > > > > for (const auto &[stream, buffer] : buffers) { > > const FrameMetadata &metadata = buffer->metadata(); > > > > - info << " " << streamNames_[stream] > > - << " seq: " << std::setw(6) << std::setfill('0') << > > metadata.sequence > > - << " bytesused: "; > > + fmt::format_to(std::back_inserter(sbuf), " {} seq: {:06} > > bytesused: ", > > + streamNames_[stream], metadata.sequence); > > > > unsigned int nplane = 0; > > for (const FrameMetadata::Plane &plane : > > metadata.planes()) { > > - info << plane.bytesused; > > + fmt::format_to(std::back_inserter(sbuf), "{}", > > plane.bytesused); > > if (++nplane < metadata.planes().size()) > > - info << "/"; > > + fmt::format_to(std::back_inserter(sbuf), > > "/"); > > } > > } > > > > @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request) > > requeue = false; > > } > > > > - std::cout << info.str() << std::endl; > > + PR("{}\n", fmt::to_string(sbuf)); > > > > if (printMetadata_) { > > const ControlList &requestMetadata = request->metadata(); > > for (const auto &[key, value] : requestMetadata) { > > const ControlId *id = controls::controls.at(key); > > - std::cout << "\t" << id->name() << " = " > > - << value.toString() << std::endl; > > + PR("\t{} = {}\n", id->name(), value.toString()); > > } > > } > > > > diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp > > index 46e34eb5..84919ab3 100644 > > --- a/src/cam/drm.cpp > > +++ b/src/cam/drm.cpp > > @@ -10,12 +10,12 @@ > > #include <algorithm> > > #include <errno.h> > > #include <fcntl.h> > > -#include <iostream> > > #include <set> > > #include <string.h> > > #include <sys/ioctl.h> > > #include <sys/stat.h> > > #include <sys/types.h> > > +#include <fmt/core.h> > > > > #include <libcamera/framebuffer.h> > > #include <libcamera/geometry.h> > > @@ -25,6 +25,9 @@ > > > > #include "event_loop.h" > > > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > + > > namespace DRM { > > > > Object::Object(Device *dev, uint32_t id, Type type) > > @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const > > drmModeConnector *connector) > > { > > auto typeName = connectorTypeNames.find(connector->connector_type); > > if (typeName == connectorTypeNames.end()) { > > - std::cerr > > - << "Invalid connector type " > > - << connector->connector_type << std::endl; > > + EPR("Invalid connector type {}}n", > > connector->connector_type); > > typeName = > > connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown); > > } > > > > @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const > > drmModeConnector *connector) > > return e.id() == > > encoderId; > > }); > > if (encoder == encoders.end()) { > > - std::cerr > > - << "Encoder " << encoderId << " not found" > > - << std::endl; > > + EPR("Encoder {} not found\n", encoderId); > > continue; > > } > > > > @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer() > > > > if (ret == -1) { > > ret = -errno; > > - std::cerr > > - << "Failed to close GEM object: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to close GEM object: {}\n", > > strerror(-ret)); > > } > > } > > > > @@ -408,9 +405,8 @@ int Device::init() > > fd_ = open(name, O_RDWR | O_CLOEXEC); > > if (fd_ < 0) { > > ret = -errno; > > - std::cerr > > - << "Failed to open DRM/KMS device " << name << ": " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to open DRM/KMS device {}: {}\n", name, > > + strerror(-ret)); > > return ret; > > } > > > > @@ -421,9 +417,7 @@ int Device::init() > > ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1); > > if (ret < 0) { > > ret = -errno; > > - std::cerr > > - << "Failed to enable atomic capability: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to enable atomic capability: {}\n", > > strerror(-ret)); > > return ret; > > } > > > > @@ -448,9 +442,7 @@ int Device::getResources() > > }; > > if (!resources) { > > ret = -errno; > > - std::cerr > > - << "Failed to get DRM/KMS resources: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to get DRM/KMS resources: {}\n", > > strerror(-ret)); > > return ret; > > } > > > > @@ -458,9 +450,7 @@ int Device::getResources() > > drmModeCrtc *crtc = drmModeGetCrtc(fd_, > > resources->crtcs[i]); > > if (!crtc) { > > ret = -errno; > > - std::cerr > > - << "Failed to get CRTC: " << strerror(-ret) > > - << std::endl; > > + EPR("Failed to get CRTC: {}\n", strerror(-ret)); > > return ret; > > } > > > > @@ -476,9 +466,7 @@ int Device::getResources() > > drmModeGetEncoder(fd_, resources->encoders[i]); > > if (!encoder) { > > ret = -errno; > > - std::cerr > > - << "Failed to get encoder: " << > > strerror(-ret) > > - << std::endl; > > + EPR("Failed to get encoder: {}\n", strerror(-ret)); > > return ret; > > } > > > > @@ -494,9 +482,7 @@ int Device::getResources() > > drmModeGetConnector(fd_, resources->connectors[i]); > > if (!connector) { > > ret = -errno; > > - std::cerr > > - << "Failed to get connector: " << > > strerror(-ret) > > - << std::endl; > > + EPR("Failed to get connector: {}\n", > > strerror(-ret)); > > return ret; > > } > > > > @@ -513,9 +499,7 @@ int Device::getResources() > > }; > > if (!planes) { > > ret = -errno; > > - std::cerr > > - << "Failed to get DRM/KMS planes: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to get DRM/KMS planes: {}\n", strerror(-ret)); > > return ret; > > } > > > > @@ -524,9 +508,7 @@ int Device::getResources() > > drmModeGetPlane(fd_, planes->planes[i]); > > if (!plane) { > > ret = -errno; > > - std::cerr > > - << "Failed to get plane: " << > > strerror(-ret) > > - << std::endl; > > + EPR("Failed to get plane: {}\n", strerror(-ret)); > > return ret; > > } > > > > @@ -556,9 +538,7 @@ int Device::getResources() > > drmModePropertyRes *property = drmModeGetProperty(fd_, id); > > if (!property) { > > ret = -errno; > > - std::cerr > > - << "Failed to get property: " << > > strerror(-ret) > > - << std::endl; > > + EPR("Failed to get property: {}\n", > > strerror(-ret)); > > continue; > > } > > > > @@ -573,9 +553,8 @@ int Device::getResources() > > for (auto &object : objects_) { > > ret = object.second->setup(); > > if (ret < 0) { > > - std::cerr > > - << "Failed to setup object " << > > object.second->id() > > - << ": " << strerror(-ret) << std::endl; > > + EPR("Failed to setup object {}: {}\n", > > + object.second->id(), strerror(-ret)); > > return ret; > > } > > } > > @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( > > ret = drmPrimeFDToHandle(fd_, plane.fd.get(), > > &handle); > > if (ret < 0) { > > ret = -errno; > > - std::cerr > > - << "Unable to import framebuffer > > dmabuf: " > > - << strerror(-ret) << std::endl; > > + EPR("Unable to import framebuffer dmabuf: > > {}\n", > > + strerror(-ret)); > > return nullptr; > > } > > > > @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( > > strides.data(), offsets, &fb->id_, 0); > > if (ret < 0) { > > ret = -errno; > > - std::cerr > > - << "Failed to add framebuffer: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to add framebuffer: {}\n", strerror(-ret)); > > return nullptr; > > } > > > > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp > > index e25784c0..87aaf59a 100644 > > --- a/src/cam/event_loop.cpp > > +++ b/src/cam/event_loop.cpp > > @@ -10,7 +10,10 @@ > > #include <assert.h> > > #include <event2/event.h> > > #include <event2/thread.h> > > -#include <iostream> > > +#include <fmt/core.h> > > + > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > EventLoop *EventLoop::instance_ = nullptr; > > > > @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type, > > event->event_ = event_new(base_, fd, events, > > &EventLoop::Event::dispatch, > > event.get()); > > if (!event->event_) { > > - std::cerr << "Failed to create event for fd " << fd << > > std::endl; > > + EPR("Failed to create event for fd {}\n", fd); > > return; > > } > > > > int ret = event_add(event->event_, nullptr); > > if (ret < 0) { > > - std::cerr << "Failed to add event for fd " << fd << > > std::endl; > > + EPR("Failed to add event for fd {}\n", fd); > > return; > > } > > > > diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp > > index 45213d4a..86e2118c 100644 > > --- a/src/cam/file_sink.cpp > > +++ b/src/cam/file_sink.cpp > > @@ -7,11 +7,12 @@ > > > > #include <assert.h> > > #include <fcntl.h> > > -#include <iomanip> > > -#include <iostream> > > -#include <sstream> > > #include <string.h> > > #include <unistd.h> > > +#include <fmt/core.h> > > + > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > #include <libcamera/camera.h> > > > > @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream, > > FrameBuffer *buffer) > > > > pos = filename.find_first_of('#'); > > if (pos != std::string::npos) { > > - std::stringstream ss; > > - ss << streamNames_[stream] << "-" << std::setw(6) > > - << std::setfill('0') << buffer->metadata().sequence; > > - filename.replace(pos, 1, ss.str()); > > + auto s = fmt::format("{}-{:06}", > > + streamNames_[stream], > > + buffer->metadata().sequence); > > + filename.replace(pos, 1, s); > > } > > > > fd = open(filename.c_str(), O_CREAT | O_WRONLY | > > @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream, > > FrameBuffer *buffer) > > S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | > > S_IWOTH); > > if (fd == -1) { > > ret = -errno; > > - std::cerr << "failed to open file " << filename << ": " > > - << strerror(-ret) << std::endl; > > + EPR("failed to open file {}: {}\n", filename, > > strerror(-ret)); > > return; > > } > > > > @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream, > > FrameBuffer *buffer) > > unsigned int length = std::min<unsigned > > int>(meta.bytesused, data.size()); > > > > if (meta.bytesused > data.size()) > > - std::cerr << "payload size " << meta.bytesused > > - << " larger than plane size " << > > data.size() > > - << std::endl; > > + EPR("payload size {} larger than plane size {}\n", > > + meta.bytesused, data.size()); > > > > ret = ::write(fd, data.data(), length); > > if (ret < 0) { > > ret = -errno; > > - std::cerr << "write error: " << strerror(-ret) > > - << std::endl; > > + EPR("write error: {}\n", strerror(-ret)); > > break; > > } else if (ret != (int)length) { > > - std::cerr << "write error: only " << ret > > - << " bytes written instead of " > > - << length << std::endl; > > + EPR("write error: only {} bytes written instead of > > {}\n", > > + ret, length); > > break; > > } > > } > > diff --git a/src/cam/image.cpp b/src/cam/image.cpp > > index fe2cc6da..73bcf915 100644 > > --- a/src/cam/image.cpp > > +++ b/src/cam/image.cpp > > @@ -9,11 +9,14 @@ > > > > #include <assert.h> > > #include <errno.h> > > -#include <iostream> > > #include <map> > > #include <string.h> > > #include <sys/mman.h> > > #include <unistd.h> > > +#include <fmt/core.h> > > + > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > using namespace libcamera; > > > > @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const > > FrameBuffer *buffer, MapMode > > > > if (plane.offset > length || > > plane.offset + plane.length > length) { > > - std::cerr << "plane is out of buffer: buffer > > length=" > > - << length << ", plane offset=" << > > plane.offset > > - << ", plane length=" << plane.length > > - << std::endl; > > + EPR("plane is out of buffer: buffer length={}, > > plane offset={}, plane length={}\n", > > + length, plane.offset, plane.length); > > return nullptr; > > } > > size_t &mapLength = mappedBuffers[fd].mapLength; > > @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const > > FrameBuffer *buffer, MapMode > > MAP_SHARED, fd, 0); > > if (address == MAP_FAILED) { > > int error = -errno; > > - std::cerr << "Failed to mmap plane: " > > - << strerror(-error) << std::endl; > > + EPR("Failed to mmap plane: {}\n", > > + strerror(-error)); > > return nullptr; > > } > > > > diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp > > index 7add81a6..823b75e4 100644 > > --- a/src/cam/kms_sink.cpp > > +++ b/src/cam/kms_sink.cpp > > @@ -10,10 +10,13 @@ > > #include <array> > > #include <algorithm> > > #include <assert.h> > > -#include <iostream> > > #include <memory> > > #include <stdint.h> > > #include <string.h> > > +#include <fmt/core.h> > > + > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > #include <libcamera/camera.h> > > #include <libcamera/formats.h> > > @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName) > > > > if (!connector_) { > > if (!connectorName.empty()) > > - std::cerr > > - << "Connector " << connectorName << " not > > found" > > - << std::endl; > > + EPR("Connector {} not found\n", connectorName); > > else > > - std::cerr << "No connected connector found" << > > std::endl; > > + EPR("No connected connector found\n"); > > return; > > } > > > > @@ -119,7 +120,7 @@ int KMSSink::configure(const > > libcamera::CameraConfiguration &config) > > mode.vdisplay == > > cfg.size.height; > > }); > > if (iter == modes.end()) { > > - std::cerr << "No mode matching " << cfg.size << std::endl; > > + EPR("No mode matching {}\n", cfg.size); > > return -EINVAL; > > } > > > > @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const > > libcamera::PixelFormat &format) > > { > > const int ret = selectPipeline(format); > > if (ret) { > > - std::cerr > > - << "Unable to find display pipeline for format " > > - << format << std::endl; > > - > > + EPR("Unable to find display pipeline for format {}\n", > > format); > > return ret; > > } > > > > - std::cout > > - << "Using KMS plane " << plane_->id() << ", CRTC " << > > crtc_->id() > > - << ", connector " << connector_->name() > > - << " (" << connector_->id() << ")" << std::endl; > > + PR("Using KMS plane {}, CRTC {}, connector {} ({})\n", > > + plane_->id(), crtc_->id(), connector_->name(), > > connector_->id()); > > > > return 0; > > } > > @@ -228,9 +224,8 @@ int KMSSink::start() > > > > ret = request->commit(DRM::AtomicRequest::FlagAllowModeset); > > if (ret < 0) { > > - std::cerr > > - << "Failed to disable CRTCs and planes: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to disable CRTCs and planes: {}\n", > > + strerror(-ret)); > > return ret; > > } > > > > @@ -250,9 +245,7 @@ int KMSSink::stop() > > > > int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset); > > if (ret < 0) { > > - std::cerr > > - << "Failed to stop display pipeline: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to stop display pipeline: {}\n", > > strerror(-ret)); > > return ret; > > } > > > > @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request > > *camRequest) > > if (!queued_) { > > int ret = drmRequest->commit(flags); > > if (ret < 0) { > > - std::cerr > > - << "Failed to commit atomic request: " > > - << strerror(-ret) << std::endl; > > + EPR("Failed to commit atomic request: {}\n", > > + strerror(-ret)); > > /* \todo Implement error handling */ > > } > > > > diff --git a/src/cam/main.cpp b/src/cam/main.cpp > > index c7f664b9..03615dc9 100644 > > --- a/src/cam/main.cpp > > +++ b/src/cam/main.cpp > > @@ -6,10 +6,9 @@ > > */ > > > > #include <atomic> > > -#include <iomanip> > > -#include <iostream> > > #include <signal.h> > > #include <string.h> > > +#include <fmt/core.h> > > > > #include <libcamera/libcamera.h> > > #include <libcamera/property_ids.h> > > @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv) > > > > ret = cm_->start(); > > if (ret) { > > - std::cout << "Failed to start camera manager: " > > - << strerror(-ret) << std::endl; > > + fmt::print("Failed to start camera manager: {}\n", -ret); > > return ret; > > } > > > > @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[]) > > > > void CamApp::cameraAdded(std::shared_ptr<Camera> cam) > > { > > - std::cout << "Camera Added: " << cam->id() << std::endl; > > + fmt::print("Camera Added: {}\n", cam->id()); > > } > > > > void CamApp::cameraRemoved(std::shared_ptr<Camera> cam) > > { > > - std::cout << "Camera Removed: " << cam->id() << std::endl; > > + fmt::print("Camera Removed: {}\n", cam->id()); > > } > > > > void CamApp::captureDone() > > @@ -193,11 +191,11 @@ int CamApp::run() > > > > /* 1. List all cameras. */ > > if (options_.isSet(OptList)) { > > - std::cout << "Available cameras:" << std::endl; > > + fmt::print("Available cameras:\n"); > > > > unsigned int index = 1; > > for (const std::shared_ptr<Camera> &cam : cm_->cameras()) { > > - std::cout << index << ": " << > > cameraName(cam.get()) << std::endl; > > + fmt::print("{}: {}\n", cameraName(cam.get()), > > index); > > index++; > > } > > } > > @@ -215,12 +213,12 @@ int CamApp::run() > > index, > > > > camera.children()); > > if (!session->isValid()) { > > - std::cout << "Failed to create camera > > session" << std::endl; > > + fmt::print("Failed to create camera > > session\n"); > > return -EINVAL; > > } > > > > - std::cout << "Using camera " << > > session->camera()->id() > > - << " as cam" << index << std::endl; > > + fmt::print("Using camera{} as cam{}\n", > > + session->camera()->id(), index); > > > > session->captureDone.connect(this, > > &CamApp::captureDone); > > > > @@ -250,7 +248,7 @@ int CamApp::run() > > > > ret = session->start(); > > if (ret) { > > - std::cout << "Failed to start camera session" << > > std::endl; > > + fmt::print("Failed to start camera session\n"); > > return ret; > > } > > > > @@ -259,8 +257,8 @@ int CamApp::run() > > > > /* 5. Enable hotplug monitoring. */ > > if (options_.isSet(OptMonitor)) { > > - std::cout << "Monitoring new hotplug and unplug events" << > > std::endl; > > - std::cout << "Press Ctrl-C to interrupt" << std::endl; > > + fmt::print("Monitoring new hotplug and unplug events\n"); > > + fmt::print("Press Ctrl-C to interrupt\n"); > > > > cm_->cameraAdded.connect(this, &CamApp::cameraAdded); > > cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved); > > @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera) > > > > void signalHandler([[maybe_unused]] int signal) > > { > > - std::cout << "Exiting" << std::endl; > > + fmt::print("Exiting"); > > CamApp::instance()->quit(); > > } > > > > diff --git a/src/cam/meson.build b/src/cam/meson.build > > index 5bab8c9e..2b47383d 100644 > > --- a/src/cam/meson.build > > +++ b/src/cam/meson.build > > @@ -7,6 +7,8 @@ if not libevent.found() > > subdir_done() > > endif > > > > +libfmt_dep = dependency('fmt') > > + > > cam_enabled = true > > > > cam_sources = files([ > > @@ -25,7 +27,7 @@ cam_cpp_args = [] > > libdrm = dependency('libdrm', required : false) > > > > if libdrm.found() > > - cam_cpp_args += [ '-DHAVE_KMS' ] > > + cam_cpp_args += [ '-DHAVE_KMS', ] > > cam_sources += files([ > > 'drm.cpp', > > 'kms_sink.cpp' > > @@ -38,6 +40,7 @@ cam = executable('cam', cam_sources, > > libcamera_public, > > libdrm, > > libevent, > > + libfmt_dep, > > ], > > cpp_args : cam_cpp_args, > > install : true) > > diff --git a/src/cam/options.cpp b/src/cam/options.cpp > > index 4f7e8691..c9979385 100644 > > --- a/src/cam/options.cpp > > +++ b/src/cam/options.cpp > > @@ -7,9 +7,11 @@ > > > > #include <assert.h> > > #include <getopt.h> > > -#include <iomanip> > > -#include <iostream> > > #include <string.h> > > +#include <fmt/core.h> > > + > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > #include "options.h" > > > > @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const > > char *arguments) > > continue; > > > > if (optionsMap_.find(key) == optionsMap_.end()) { > > - std::cerr << "Invalid option " << key << std::endl; > > + EPR("Invalid option {}\n", key); > > return options; > > } > > > > OptionArgument arg = optionsMap_[key].argument; > > if (value.empty() && arg == ArgumentRequired) { > > - std::cerr << "Option " << key << " requires an > > argument" > > - << std::endl; > > + EPR("Option {} requires an argument\n", key); > > return options; > > } else if (!value.empty() && arg == ArgumentNone) { > > - std::cerr << "Option " << key << " takes no > > argument" > > - << std::endl; > > + EPR("Option {} takes no argument\n", key); > > return options; > > } > > > > const Option &option = optionsMap_[key]; > > if (!options.parseValue(key, option, value.c_str())) { > > - std::cerr << "Failed to parse '" << value << "' as > > " > > - << option.typeName() << " for option " > > << key > > - << std::endl; > > + EPR("Failed to parse '{}' as {} for option {}\n", > > + value, option.typeName(), key); > > return options; > > } > > } > > @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent) > > argument += "]"; > > } > > > > - std::cerr << std::setw(indent) << argument; > > + EPR("{:{}}", argument, indent); > > > > for (const char *help = option.help, *end = help; end;) { > > end = strchr(help, '\n'); > > if (end) { > > - std::cerr << std::string(help, end - help > > + 1); > > - std::cerr << std::setw(indent) << " "; > > + EPR(std::string(help, end - help + 1)); > > + EPR("{:{}}", "", indent); > > help = end + 1; > > } else { > > - std::cerr << help << std::endl; > > + EPR("{}\n", help); > > } > > } > > } > > @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int > > argc, char **argv) > > > > if (c == '?' || c == ':') { > > if (c == '?') > > - std::cerr << "Invalid option "; > > + EPR("Invalid option "); > > else > > - std::cerr << "Missing argument for option > > "; > > - std::cerr << argv[optind - 1] << std::endl; > > + EPR("Missing argument for option "); > > + EPR("{}\n", argv[optind - 1]); > > > > usage(); > > return options; > > @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc, > > char **argv) > > } > > > > if (optind < argc) { > > - std::cerr << "Invalid non-option argument '" << > > argv[optind] > > - << "'" << std::endl; > > + EPR("Invalid non-option argument '{}'\n", argv[optind]); > > usage(); > > return options; > > } > > @@ -992,14 +990,9 @@ void OptionsParser::usage() > > > > indent = (indent + 7) / 8 * 8; > > > > - std::cerr << "Options:" << std::endl; > > - > > - std::ios_base::fmtflags f(std::cerr.flags()); > > - std::cerr << std::left; > > + EPR("Options:\n"); > > > > usageOptions(options_, indent); > > - > > - std::cerr.flags(f); > > } > > > > void OptionsParser::usageOptions(const std::list<Option> &options, > > @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const > > std::list<Option> &options, > > if (option.isArray) > > argument += " ..."; > > > > - std::cerr << std::setw(indent) << argument; > > + EPR("{:{}}", argument, indent); > > > > - for (const char *help = option.help, *end = help; end; ) { > > + for (const char *help = option.help, *end = help; end;) { > > end = strchr(help, '\n'); > > if (end) { > > - std::cerr << std::string(help, end - help > > + 1); > > - std::cerr << std::setw(indent) << " "; > > + EPR(std::string(help, end - help + 1)); > > + EPR("{:{}}", "", indent); > > help = end + 1; > > } else { > > - std::cerr << help << std::endl; > > + EPR("{}\n", help); > > } > > } > > > > @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const > > std::list<Option> &options, > > return; > > > > for (const Option *option : parentOptions) { > > - std::cerr << std::endl << "Options valid in the context of > > " > > - << option->optionName() << ":" << std::endl; > > + EPR("\nOptions valid in the context of {}:\n", > > + option->optionName()); > > usageOptions(option->children, indent); > > } > > } > > @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option > > &option, const char *arg, > > > > std::tie(options, error) = childOption(option.parent, options); > > if (error) { > > - std::cerr << "Option " << option.optionName() << " > > requires a " > > - << error->optionName() << " context" << > > std::endl; > > + EPR("Option {} requires a {} context\n", > > + option.optionName(), error->optionName()); > > return false; > > } > > > > if (!options->parseValue(option.opt, option, arg)) { > > - std::cerr << "Can't parse " << option.typeName() > > - << " argument for option " << option.optionName() > > - << std::endl; > > + EPR("Can't parse {} argument for option {}\n", > > + option.typeName(), option.optionName()); > > return false; > > } > > > > diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp > > index 150bd27c..666862eb 100644 > > --- a/src/cam/stream_options.cpp > > +++ b/src/cam/stream_options.cpp > > @@ -6,7 +6,10 @@ > > */ > > #include "stream_options.h" > > > > -#include <iostream> > > +#include <fmt/core.h> > > + > > +#define PR(...) fmt::print(__VA_ARGS__) > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > using namespace libcamera; > > > > @@ -30,8 +33,7 @@ KeyValueParser::Options > > StreamKeyValueParser::parse(const char *arguments) > > > > if (options.valid() && options.isSet("role") && > > !parseRole(&role, options)) { > > - std::cerr << "Unknown stream role " > > - << options["role"].toString() << std::endl; > > + EPR("Unknown stream role {}\n", > > options["role"].toString()); > > options.invalidate(); > > } > > > > @@ -64,7 +66,7 @@ int > > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, > > const OptionValue &values) > > { > > if (!config) { > > - std::cerr << "No configuration provided" << std::endl; > > + EPR("No configuration provided\n"); > > return -EINVAL; > > } > > > > @@ -75,12 +77,8 @@ int > > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, > > const std::vector<OptionValue> &streamParameters = > > values.toArray(); > > > > if (config->size() != streamParameters.size()) { > > - std::cerr > > - << "Number of streams in configuration " > > - << config->size() > > - << " does not match number of streams parsed " > > - << streamParameters.size() > > - << std::endl; > > + EPR("Number of streams in configuration {} does not match > > number of streams parsed {}\n", > > + config->size(), streamParameters.size()); > > return -EINVAL; > > } > > > > -- > > 2.34.1 > > > >
On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel <libcamera-devel@lists.libcamera.org> wrote: > > Quoting Naushir Patuck (2022-05-10 10:49:14) > > Hi Tomi, > > > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel < > > libcamera-devel@lists.libcamera.org> wrote: > > > > > This is just a conversation starter, not for merging. I really like > > > libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > is the first library I add to any new C++ project I make. > > > > > > You can find more information about libfmt from: > > > > > > https://github.com/fmtlib/fmt > > > https://fmt.dev/latest/index.html > > > > > > This patch is just a crude conversion with ugly macros to showcase what > > > the formatting code might look like. > > > > > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > than iostreams, but for the size it didn't seem to be true in cam's case > > > as the tests below show. However, simple prints did reduce the exe size, > > > but the few more complex ones increased the size. > > > > > > Size tests with gcc 11.2.0-19ubuntu1 > > > > > > - Without libfmt > > > > > > debug 3523400 > > > debug lto 3269368 > > > release 223056 > > > release lto 172280 > > > > > > - With libfmt > > > > > > debug 4424256 > > > debug lto 4143840 > > > release 303952 > > > release lto 252640 > > > > > > Above shows that cam's size clearly increases with libfmt. However, the > > > increase really comes only from one case, the use of fmt::memory_buffer > > > and std::back_inserter. Converting that code to use fmt::format() and > > > naive string append gives: > > > > > > release with string append 233680 > > > release lto with string append 186936 > > > > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter > > > to another file, I see much less increase in the size: > > > > > > release lto with two uses of memory_buffer, back_inserter 256736 > > > > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > > > > > > > For what it's worth, I absolutely loathe formatting in std iostream, and > > libfmt is a wonderful alternative. > > It also is close enough to the C++20 std::format implementation that > > eventual porting would be low effort. So I am all for this change :) I am all in for this change also, personally I would have changed to printf for now to have one less dependency (also an easy port to C++20 std::format). The dependency list is getting large, I noticed a build of mine failing recently because I didn't have libyaml. But std::format and libfmt are quite fast and anything is better than streams so +1. > > I've never used (yet) {fmt}, but I've only heard good things about > performance, and of course it's headed into the standard, so I also > think there is some good merit to be found in this development. > > -- > Kieran > > > > > > Regards, > > Naush > > > > > > > > > --- > > > src/cam/camera_session.cpp | 105 ++++++++++++++++--------------------- > > > src/cam/drm.cpp | 68 ++++++++---------------- > > > src/cam/event_loop.cpp | 9 ++-- > > > src/cam/file_sink.cpp | 31 +++++------ > > > src/cam/image.cpp | 15 +++--- > > > src/cam/kms_sink.cpp | 38 ++++++-------- > > > src/cam/main.cpp | 28 +++++----- > > > src/cam/meson.build | 5 +- > > > src/cam/options.cpp | 66 ++++++++++------------- > > > src/cam/stream_options.cpp | 18 +++---- > > > 10 files changed, 165 insertions(+), 218 deletions(-) > > > > > > diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp > > > index efffafbf..7843c3fd 100644 > > > --- a/src/cam/camera_session.cpp > > > +++ b/src/cam/camera_session.cpp > > > @@ -5,10 +5,9 @@ > > > * camera_session.cpp - Camera capture session > > > */ > > > > > > -#include <iomanip> > > > -#include <iostream> > > > #include <limits.h> > > > -#include <sstream> > > > +#include <fmt/format.h> > > > +#include <fmt/ostream.h> > > > > > > #include <libcamera/control_ids.h> > > > #include <libcamera/property_ids.h> > > > @@ -22,6 +21,9 @@ > > > #include "main.h" > > > #include "stream_options.h" > > > > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > + > > > using namespace libcamera; > > > > > > CameraSession::CameraSession(CameraManager *cm, > > > @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm, > > > camera_ = cm->get(cameraId); > > > > > > if (!camera_) { > > > - std::cerr << "Camera " << cameraId << " not found" << > > > std::endl; > > > + EPR("Camera {} not found\n", cameraId); > > > return; > > > } > > > > > > if (camera_->acquire()) { > > > - std::cerr << "Failed to acquire camera " << cameraId > > > - << std::endl; > > > + EPR("Failed to acquire camera {}", cameraId); > > > return; > > > } > > > > > > @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm, > > > std::unique_ptr<CameraConfiguration> config = > > > camera_->generateConfiguration(roles); > > > if (!config || config->size() != roles.size()) { > > > - std::cerr << "Failed to get default stream configuration" > > > - << std::endl; > > > + EPR("Failed to get default stream configuration\n"); > > > return; > > > } > > > > > > /* Apply configuration if explicitly requested. */ > > > if (StreamKeyValueParser::updateConfiguration(config.get(), > > > > > > options_[OptStream])) { > > > - std::cerr << "Failed to update configuration" << std::endl; > > > + EPR("Failed to update configuration\n"); > > > return; > > > } > > > > > > @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm, > > > #ifdef HAVE_KMS > > > if (options_.isSet(OptDisplay)) { > > > if (options_.isSet(OptFile)) { > > > - std::cerr << "--display and --file options are > > > mutually exclusive" > > > - << std::endl; > > > + EPR("--display and --file options are mutually > > > exclusive\n"); > > > return; > > > } > > > > > > if (roles.size() != 1) { > > > - std::cerr << "Display doesn't support multiple > > > streams" > > > - << std::endl; > > > + EPR("Display doesn't support multiple streams\n"); > > > return; > > > } > > > > > > if (roles[0] != StreamRole::Viewfinder) { > > > - std::cerr << "Display requires a viewfinder stream" > > > - << std::endl; > > > + EPR("Display requires a viewfinder stream\n"); > > > return; > > > } > > > } > > > @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm, > > > > > > case CameraConfiguration::Adjusted: > > > if (strictFormats) { > > > - std::cout << "Adjusting camera configuration > > > disallowed by --strict-formats argument" > > > - << std::endl; > > > + PR("Adjusting camera configuration disallowed by > > > --strict-formats argument\n"); > > > return; > > > } > > > - std::cout << "Camera configuration adjusted" << std::endl; > > > + PR("Camera configuration adjusted\n"); > > > break; > > > > > > case CameraConfiguration::Invalid: > > > - std::cout << "Camera configuration invalid" << std::endl; > > > + PR("Camera configuration invalid\n"); > > > return; > > > } > > > > > > @@ -121,8 +117,7 @@ CameraSession::~CameraSession() > > > void CameraSession::listControls() const > > > { > > > for (const auto &[id, info] : camera_->controls()) { > > > - std::cout << "Control: " << id->name() << ": " > > > - << info.toString() << std::endl; > > > + PR("Control: {}: {}}n", id->name(), info.toString()); > > > } > > > } > > > > > > @@ -131,8 +126,7 @@ void CameraSession::listProperties() const > > > for (const auto &[key, value] : camera_->properties()) { > > > const ControlId *id = properties::properties.at(key); > > > > > > - std::cout << "Property: " << id->name() << " = " > > > - << value.toString() << std::endl; > > > + PR("Property: {} = {}\n", id->name(), value.toString()); > > > } > > > } > > > > > > @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const > > > { > > > unsigned int index = 0; > > > for (const StreamConfiguration &cfg : *config_) { > > > - std::cout << index << ": " << cfg.toString() << std::endl; > > > + PR("{}: {}\n", index, cfg.toString()); > > > > > > const StreamFormats &formats = cfg.formats(); > > > for (PixelFormat pixelformat : formats.pixelformats()) { > > > - std::cout << " * Pixelformat: " > > > - << pixelformat << " " > > > - << formats.range(pixelformat).toString() > > > - << std::endl; > > > + PR(" * Pixelformat: {} {}\n", pixelformat, > > > + formats.range(pixelformat).toString()); > > > > > > for (const Size &size : formats.sizes(pixelformat)) > > > - std::cout << " - " << size << std::endl; > > > + PR(" - {}\n", size); > > > } > > > > > > index++; > > > @@ -168,7 +160,7 @@ int CameraSession::start() > > > > > > ret = camera_->configure(config_.get()); > > > if (ret < 0) { > > > - std::cout << "Failed to configure camera" << std::endl; > > > + PR("Failed to configure camera\n"); > > > return ret; > > > } > > > > > > @@ -197,8 +189,7 @@ int CameraSession::start() > > > if (sink_) { > > > ret = sink_->configure(*config_); > > > if (ret < 0) { > > > - std::cout << "Failed to configure frame sink" > > > - << std::endl; > > > + PR("Failed to configure frame sink\n"); > > > return ret; > > > } > > > > > > @@ -214,12 +205,12 @@ void CameraSession::stop() > > > { > > > int ret = camera_->stop(); > > > if (ret) > > > - std::cout << "Failed to stop capture" << std::endl; > > > + PR("Failed to stop capture\n"); > > > > > > if (sink_) { > > > ret = sink_->stop(); > > > if (ret) > > > - std::cout << "Failed to stop frame sink" << > > > std::endl; > > > + PR("Failed to stop frame sink\n"); > > > } > > > > > > sink_.reset(); > > > @@ -238,7 +229,7 @@ int CameraSession::startCapture() > > > for (StreamConfiguration &cfg : *config_) { > > > ret = allocator_->allocate(cfg.stream()); > > > if (ret < 0) { > > > - std::cerr << "Can't allocate buffers" << std::endl; > > > + EPR("Can't allocate buffers\n"); > > > return -ENOMEM; > > > } > > > > > > @@ -254,7 +245,7 @@ int CameraSession::startCapture() > > > for (unsigned int i = 0; i < nbuffers; i++) { > > > std::unique_ptr<Request> request = > > > camera_->createRequest(); > > > if (!request) { > > > - std::cerr << "Can't create request" << std::endl; > > > + EPR("Can't create request\n"); > > > return -ENOMEM; > > > } > > > > > > @@ -266,8 +257,7 @@ int CameraSession::startCapture() > > > > > > ret = request->addBuffer(stream, buffer.get()); > > > if (ret < 0) { > > > - std::cerr << "Can't set buffer for request" > > > - << std::endl; > > > + EPR("Can't set buffer for request\n"); > > > return ret; > > > } > > > > > > @@ -281,14 +271,14 @@ int CameraSession::startCapture() > > > if (sink_) { > > > ret = sink_->start(); > > > if (ret) { > > > - std::cout << "Failed to start frame sink" << > > > std::endl; > > > + PR("Failed to start frame sink\n"); > > > return ret; > > > } > > > } > > > > > > ret = camera_->start(); > > > if (ret) { > > > - std::cout << "Failed to start capture" << std::endl; > > > + PR("Failed to start capture\n"); > > > if (sink_) > > > sink_->stop(); > > > return ret; > > > @@ -297,7 +287,7 @@ int CameraSession::startCapture() > > > for (std::unique_ptr<Request> &request : requests_) { > > > ret = queueRequest(request.get()); > > > if (ret < 0) { > > > - std::cerr << "Can't queue request" << std::endl; > > > + EPR("Can't queue request\n"); > > > camera_->stop(); > > > if (sink_) > > > sink_->stop(); > > > @@ -306,13 +296,11 @@ int CameraSession::startCapture() > > > } > > > > > > if (captureLimit_) > > > - std::cout << "cam" << cameraIndex_ > > > - << ": Capture " << captureLimit_ << " frames" > > > - << std::endl; > > > + PR("cam{}: Capture {} frames\n", cameraIndex_, > > > + captureLimit_); > > > else > > > - std::cout << "cam" << cameraIndex_ > > > - << ": Capture until user interrupts by SIGINT" > > > - << std::endl; > > > + PR("cam{}: Capture until user interrupts by SIGINT\n", > > > + cameraIndex_); > > > > > > return 0; > > > } > > > @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request) > > > > > > bool requeue = true; > > > > > > - std::stringstream info; > > > - info << ts / 1000000000 << "." > > > - << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 > > > - << " (" << std::fixed << std::setprecision(2) << fps << " > > > fps)"; > > > + auto sbuf = fmt::memory_buffer(); > > > + fmt::format_to(std::back_inserter(sbuf), "{}.{:06} ({:.2f} fps)", > > > + ts / 1000000000, > > > + ts / 1000 % 1000000, > > > + fps); > > > > > > for (const auto &[stream, buffer] : buffers) { > > > const FrameMetadata &metadata = buffer->metadata(); > > > > > > - info << " " << streamNames_[stream] > > > - << " seq: " << std::setw(6) << std::setfill('0') << > > > metadata.sequence > > > - << " bytesused: "; > > > + fmt::format_to(std::back_inserter(sbuf), " {} seq: {:06} > > > bytesused: ", > > > + streamNames_[stream], metadata.sequence); > > > > > > unsigned int nplane = 0; > > > for (const FrameMetadata::Plane &plane : > > > metadata.planes()) { > > > - info << plane.bytesused; > > > + fmt::format_to(std::back_inserter(sbuf), "{}", > > > plane.bytesused); > > > if (++nplane < metadata.planes().size()) > > > - info << "/"; > > > + fmt::format_to(std::back_inserter(sbuf), > > > "/"); > > > } > > > } > > > > > > @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request) > > > requeue = false; > > > } > > > > > > - std::cout << info.str() << std::endl; > > > + PR("{}\n", fmt::to_string(sbuf)); > > > > > > if (printMetadata_) { > > > const ControlList &requestMetadata = request->metadata(); > > > for (const auto &[key, value] : requestMetadata) { > > > const ControlId *id = controls::controls.at(key); > > > - std::cout << "\t" << id->name() << " = " > > > - << value.toString() << std::endl; > > > + PR("\t{} = {}\n", id->name(), value.toString()); > > > } > > > } > > > > > > diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp > > > index 46e34eb5..84919ab3 100644 > > > --- a/src/cam/drm.cpp > > > +++ b/src/cam/drm.cpp > > > @@ -10,12 +10,12 @@ > > > #include <algorithm> > > > #include <errno.h> > > > #include <fcntl.h> > > > -#include <iostream> > > > #include <set> > > > #include <string.h> > > > #include <sys/ioctl.h> > > > #include <sys/stat.h> > > > #include <sys/types.h> > > > +#include <fmt/core.h> > > > > > > #include <libcamera/framebuffer.h> > > > #include <libcamera/geometry.h> > > > @@ -25,6 +25,9 @@ > > > > > > #include "event_loop.h" > > > > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > + > > > namespace DRM { > > > > > > Object::Object(Device *dev, uint32_t id, Type type) > > > @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const > > > drmModeConnector *connector) > > > { > > > auto typeName = connectorTypeNames.find(connector->connector_type); > > > if (typeName == connectorTypeNames.end()) { > > > - std::cerr > > > - << "Invalid connector type " > > > - << connector->connector_type << std::endl; > > > + EPR("Invalid connector type {}}n", > > > connector->connector_type); > > > typeName = > > > connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown); > > > } > > > > > > @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const > > > drmModeConnector *connector) > > > return e.id() == > > > encoderId; > > > }); > > > if (encoder == encoders.end()) { > > > - std::cerr > > > - << "Encoder " << encoderId << " not found" > > > - << std::endl; > > > + EPR("Encoder {} not found\n", encoderId); > > > continue; > > > } > > > > > > @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer() > > > > > > if (ret == -1) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to close GEM object: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to close GEM object: {}\n", > > > strerror(-ret)); > > > } > > > } > > > > > > @@ -408,9 +405,8 @@ int Device::init() > > > fd_ = open(name, O_RDWR | O_CLOEXEC); > > > if (fd_ < 0) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to open DRM/KMS device " << name << ": " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to open DRM/KMS device {}: {}\n", name, > > > + strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -421,9 +417,7 @@ int Device::init() > > > ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1); > > > if (ret < 0) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to enable atomic capability: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to enable atomic capability: {}\n", > > > strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -448,9 +442,7 @@ int Device::getResources() > > > }; > > > if (!resources) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get DRM/KMS resources: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to get DRM/KMS resources: {}\n", > > > strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -458,9 +450,7 @@ int Device::getResources() > > > drmModeCrtc *crtc = drmModeGetCrtc(fd_, > > > resources->crtcs[i]); > > > if (!crtc) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get CRTC: " << strerror(-ret) > > > - << std::endl; > > > + EPR("Failed to get CRTC: {}\n", strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -476,9 +466,7 @@ int Device::getResources() > > > drmModeGetEncoder(fd_, resources->encoders[i]); > > > if (!encoder) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get encoder: " << > > > strerror(-ret) > > > - << std::endl; > > > + EPR("Failed to get encoder: {}\n", strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -494,9 +482,7 @@ int Device::getResources() > > > drmModeGetConnector(fd_, resources->connectors[i]); > > > if (!connector) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get connector: " << > > > strerror(-ret) > > > - << std::endl; > > > + EPR("Failed to get connector: {}\n", > > > strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -513,9 +499,7 @@ int Device::getResources() > > > }; > > > if (!planes) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get DRM/KMS planes: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to get DRM/KMS planes: {}\n", strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -524,9 +508,7 @@ int Device::getResources() > > > drmModeGetPlane(fd_, planes->planes[i]); > > > if (!plane) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get plane: " << > > > strerror(-ret) > > > - << std::endl; > > > + EPR("Failed to get plane: {}\n", strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -556,9 +538,7 @@ int Device::getResources() > > > drmModePropertyRes *property = drmModeGetProperty(fd_, id); > > > if (!property) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to get property: " << > > > strerror(-ret) > > > - << std::endl; > > > + EPR("Failed to get property: {}\n", > > > strerror(-ret)); > > > continue; > > > } > > > > > > @@ -573,9 +553,8 @@ int Device::getResources() > > > for (auto &object : objects_) { > > > ret = object.second->setup(); > > > if (ret < 0) { > > > - std::cerr > > > - << "Failed to setup object " << > > > object.second->id() > > > - << ": " << strerror(-ret) << std::endl; > > > + EPR("Failed to setup object {}: {}\n", > > > + object.second->id(), strerror(-ret)); > > > return ret; > > > } > > > } > > > @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( > > > ret = drmPrimeFDToHandle(fd_, plane.fd.get(), > > > &handle); > > > if (ret < 0) { > > > ret = -errno; > > > - std::cerr > > > - << "Unable to import framebuffer > > > dmabuf: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Unable to import framebuffer dmabuf: > > > {}\n", > > > + strerror(-ret)); > > > return nullptr; > > > } > > > > > > @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( > > > strides.data(), offsets, &fb->id_, 0); > > > if (ret < 0) { > > > ret = -errno; > > > - std::cerr > > > - << "Failed to add framebuffer: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to add framebuffer: {}\n", strerror(-ret)); > > > return nullptr; > > > } > > > > > > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp > > > index e25784c0..87aaf59a 100644 > > > --- a/src/cam/event_loop.cpp > > > +++ b/src/cam/event_loop.cpp > > > @@ -10,7 +10,10 @@ > > > #include <assert.h> > > > #include <event2/event.h> > > > #include <event2/thread.h> > > > -#include <iostream> > > > +#include <fmt/core.h> > > > + > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > > > EventLoop *EventLoop::instance_ = nullptr; > > > > > > @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type, > > > event->event_ = event_new(base_, fd, events, > > > &EventLoop::Event::dispatch, > > > event.get()); > > > if (!event->event_) { > > > - std::cerr << "Failed to create event for fd " << fd << > > > std::endl; > > > + EPR("Failed to create event for fd {}\n", fd); > > > return; > > > } > > > > > > int ret = event_add(event->event_, nullptr); > > > if (ret < 0) { > > > - std::cerr << "Failed to add event for fd " << fd << > > > std::endl; > > > + EPR("Failed to add event for fd {}\n", fd); > > > return; > > > } > > > > > > diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp > > > index 45213d4a..86e2118c 100644 > > > --- a/src/cam/file_sink.cpp > > > +++ b/src/cam/file_sink.cpp > > > @@ -7,11 +7,12 @@ > > > > > > #include <assert.h> > > > #include <fcntl.h> > > > -#include <iomanip> > > > -#include <iostream> > > > -#include <sstream> > > > #include <string.h> > > > #include <unistd.h> > > > +#include <fmt/core.h> > > > + > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > > > #include <libcamera/camera.h> > > > > > > @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream, > > > FrameBuffer *buffer) > > > > > > pos = filename.find_first_of('#'); > > > if (pos != std::string::npos) { > > > - std::stringstream ss; > > > - ss << streamNames_[stream] << "-" << std::setw(6) > > > - << std::setfill('0') << buffer->metadata().sequence; > > > - filename.replace(pos, 1, ss.str()); > > > + auto s = fmt::format("{}-{:06}", > > > + streamNames_[stream], > > > + buffer->metadata().sequence); > > > + filename.replace(pos, 1, s); > > > } > > > > > > fd = open(filename.c_str(), O_CREAT | O_WRONLY | > > > @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream, > > > FrameBuffer *buffer) > > > S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | > > > S_IWOTH); > > > if (fd == -1) { > > > ret = -errno; > > > - std::cerr << "failed to open file " << filename << ": " > > > - << strerror(-ret) << std::endl; > > > + EPR("failed to open file {}: {}\n", filename, > > > strerror(-ret)); > > > return; > > > } > > > > > > @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream, > > > FrameBuffer *buffer) > > > unsigned int length = std::min<unsigned > > > int>(meta.bytesused, data.size()); > > > > > > if (meta.bytesused > data.size()) > > > - std::cerr << "payload size " << meta.bytesused > > > - << " larger than plane size " << > > > data.size() > > > - << std::endl; > > > + EPR("payload size {} larger than plane size {}\n", > > > + meta.bytesused, data.size()); > > > > > > ret = ::write(fd, data.data(), length); > > > if (ret < 0) { > > > ret = -errno; > > > - std::cerr << "write error: " << strerror(-ret) > > > - << std::endl; > > > + EPR("write error: {}\n", strerror(-ret)); > > > break; > > > } else if (ret != (int)length) { > > > - std::cerr << "write error: only " << ret > > > - << " bytes written instead of " > > > - << length << std::endl; > > > + EPR("write error: only {} bytes written instead of > > > {}\n", > > > + ret, length); > > > break; > > > } > > > } > > > diff --git a/src/cam/image.cpp b/src/cam/image.cpp > > > index fe2cc6da..73bcf915 100644 > > > --- a/src/cam/image.cpp > > > +++ b/src/cam/image.cpp > > > @@ -9,11 +9,14 @@ > > > > > > #include <assert.h> > > > #include <errno.h> > > > -#include <iostream> > > > #include <map> > > > #include <string.h> > > > #include <sys/mman.h> > > > #include <unistd.h> > > > +#include <fmt/core.h> > > > + > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > > > using namespace libcamera; > > > > > > @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const > > > FrameBuffer *buffer, MapMode > > > > > > if (plane.offset > length || > > > plane.offset + plane.length > length) { > > > - std::cerr << "plane is out of buffer: buffer > > > length=" > > > - << length << ", plane offset=" << > > > plane.offset > > > - << ", plane length=" << plane.length > > > - << std::endl; > > > + EPR("plane is out of buffer: buffer length={}, > > > plane offset={}, plane length={}\n", > > > + length, plane.offset, plane.length); > > > return nullptr; > > > } > > > size_t &mapLength = mappedBuffers[fd].mapLength; > > > @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const > > > FrameBuffer *buffer, MapMode > > > MAP_SHARED, fd, 0); > > > if (address == MAP_FAILED) { > > > int error = -errno; > > > - std::cerr << "Failed to mmap plane: " > > > - << strerror(-error) << std::endl; > > > + EPR("Failed to mmap plane: {}\n", > > > + strerror(-error)); > > > return nullptr; > > > } > > > > > > diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp > > > index 7add81a6..823b75e4 100644 > > > --- a/src/cam/kms_sink.cpp > > > +++ b/src/cam/kms_sink.cpp > > > @@ -10,10 +10,13 @@ > > > #include <array> > > > #include <algorithm> > > > #include <assert.h> > > > -#include <iostream> > > > #include <memory> > > > #include <stdint.h> > > > #include <string.h> > > > +#include <fmt/core.h> > > > + > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > > > #include <libcamera/camera.h> > > > #include <libcamera/formats.h> > > > @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName) > > > > > > if (!connector_) { > > > if (!connectorName.empty()) > > > - std::cerr > > > - << "Connector " << connectorName << " not > > > found" > > > - << std::endl; > > > + EPR("Connector {} not found\n", connectorName); > > > else > > > - std::cerr << "No connected connector found" << > > > std::endl; > > > + EPR("No connected connector found\n"); > > > return; > > > } > > > > > > @@ -119,7 +120,7 @@ int KMSSink::configure(const > > > libcamera::CameraConfiguration &config) > > > mode.vdisplay == > > > cfg.size.height; > > > }); > > > if (iter == modes.end()) { > > > - std::cerr << "No mode matching " << cfg.size << std::endl; > > > + EPR("No mode matching {}\n", cfg.size); > > > return -EINVAL; > > > } > > > > > > @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const > > > libcamera::PixelFormat &format) > > > { > > > const int ret = selectPipeline(format); > > > if (ret) { > > > - std::cerr > > > - << "Unable to find display pipeline for format " > > > - << format << std::endl; > > > - > > > + EPR("Unable to find display pipeline for format {}\n", > > > format); > > > return ret; > > > } > > > > > > - std::cout > > > - << "Using KMS plane " << plane_->id() << ", CRTC " << > > > crtc_->id() > > > - << ", connector " << connector_->name() > > > - << " (" << connector_->id() << ")" << std::endl; > > > + PR("Using KMS plane {}, CRTC {}, connector {} ({})\n", > > > + plane_->id(), crtc_->id(), connector_->name(), > > > connector_->id()); > > > > > > return 0; > > > } > > > @@ -228,9 +224,8 @@ int KMSSink::start() > > > > > > ret = request->commit(DRM::AtomicRequest::FlagAllowModeset); > > > if (ret < 0) { > > > - std::cerr > > > - << "Failed to disable CRTCs and planes: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to disable CRTCs and planes: {}\n", > > > + strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -250,9 +245,7 @@ int KMSSink::stop() > > > > > > int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset); > > > if (ret < 0) { > > > - std::cerr > > > - << "Failed to stop display pipeline: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to stop display pipeline: {}\n", > > > strerror(-ret)); > > > return ret; > > > } > > > > > > @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request > > > *camRequest) > > > if (!queued_) { > > > int ret = drmRequest->commit(flags); > > > if (ret < 0) { > > > - std::cerr > > > - << "Failed to commit atomic request: " > > > - << strerror(-ret) << std::endl; > > > + EPR("Failed to commit atomic request: {}\n", > > > + strerror(-ret)); > > > /* \todo Implement error handling */ > > > } > > > > > > diff --git a/src/cam/main.cpp b/src/cam/main.cpp > > > index c7f664b9..03615dc9 100644 > > > --- a/src/cam/main.cpp > > > +++ b/src/cam/main.cpp > > > @@ -6,10 +6,9 @@ > > > */ > > > > > > #include <atomic> > > > -#include <iomanip> > > > -#include <iostream> > > > #include <signal.h> > > > #include <string.h> > > > +#include <fmt/core.h> > > > > > > #include <libcamera/libcamera.h> > > > #include <libcamera/property_ids.h> > > > @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv) > > > > > > ret = cm_->start(); > > > if (ret) { > > > - std::cout << "Failed to start camera manager: " > > > - << strerror(-ret) << std::endl; > > > + fmt::print("Failed to start camera manager: {}\n", -ret); > > > return ret; > > > } > > > > > > @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[]) > > > > > > void CamApp::cameraAdded(std::shared_ptr<Camera> cam) > > > { > > > - std::cout << "Camera Added: " << cam->id() << std::endl; > > > + fmt::print("Camera Added: {}\n", cam->id()); > > > } > > > > > > void CamApp::cameraRemoved(std::shared_ptr<Camera> cam) > > > { > > > - std::cout << "Camera Removed: " << cam->id() << std::endl; > > > + fmt::print("Camera Removed: {}\n", cam->id()); > > > } > > > > > > void CamApp::captureDone() > > > @@ -193,11 +191,11 @@ int CamApp::run() > > > > > > /* 1. List all cameras. */ > > > if (options_.isSet(OptList)) { > > > - std::cout << "Available cameras:" << std::endl; > > > + fmt::print("Available cameras:\n"); > > > > > > unsigned int index = 1; > > > for (const std::shared_ptr<Camera> &cam : cm_->cameras()) { > > > - std::cout << index << ": " << > > > cameraName(cam.get()) << std::endl; > > > + fmt::print("{}: {}\n", cameraName(cam.get()), > > > index); > > > index++; > > > } > > > } > > > @@ -215,12 +213,12 @@ int CamApp::run() > > > index, > > > > > > camera.children()); > > > if (!session->isValid()) { > > > - std::cout << "Failed to create camera > > > session" << std::endl; > > > + fmt::print("Failed to create camera > > > session\n"); > > > return -EINVAL; > > > } > > > > > > - std::cout << "Using camera " << > > > session->camera()->id() > > > - << " as cam" << index << std::endl; > > > + fmt::print("Using camera{} as cam{}\n", > > > + session->camera()->id(), index); > > > > > > session->captureDone.connect(this, > > > &CamApp::captureDone); > > > > > > @@ -250,7 +248,7 @@ int CamApp::run() > > > > > > ret = session->start(); > > > if (ret) { > > > - std::cout << "Failed to start camera session" << > > > std::endl; > > > + fmt::print("Failed to start camera session\n"); > > > return ret; > > > } > > > > > > @@ -259,8 +257,8 @@ int CamApp::run() > > > > > > /* 5. Enable hotplug monitoring. */ > > > if (options_.isSet(OptMonitor)) { > > > - std::cout << "Monitoring new hotplug and unplug events" << > > > std::endl; > > > - std::cout << "Press Ctrl-C to interrupt" << std::endl; > > > + fmt::print("Monitoring new hotplug and unplug events\n"); > > > + fmt::print("Press Ctrl-C to interrupt\n"); > > > > > > cm_->cameraAdded.connect(this, &CamApp::cameraAdded); > > > cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved); > > > @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera) > > > > > > void signalHandler([[maybe_unused]] int signal) > > > { > > > - std::cout << "Exiting" << std::endl; > > > + fmt::print("Exiting"); > > > CamApp::instance()->quit(); > > > } > > > > > > diff --git a/src/cam/meson.build b/src/cam/meson.build > > > index 5bab8c9e..2b47383d 100644 > > > --- a/src/cam/meson.build > > > +++ b/src/cam/meson.build > > > @@ -7,6 +7,8 @@ if not libevent.found() > > > subdir_done() > > > endif > > > > > > +libfmt_dep = dependency('fmt') > > > + > > > cam_enabled = true > > > > > > cam_sources = files([ > > > @@ -25,7 +27,7 @@ cam_cpp_args = [] > > > libdrm = dependency('libdrm', required : false) > > > > > > if libdrm.found() > > > - cam_cpp_args += [ '-DHAVE_KMS' ] > > > + cam_cpp_args += [ '-DHAVE_KMS', ] > > > cam_sources += files([ > > > 'drm.cpp', > > > 'kms_sink.cpp' > > > @@ -38,6 +40,7 @@ cam = executable('cam', cam_sources, > > > libcamera_public, > > > libdrm, > > > libevent, > > > + libfmt_dep, > > > ], > > > cpp_args : cam_cpp_args, > > > install : true) > > > diff --git a/src/cam/options.cpp b/src/cam/options.cpp > > > index 4f7e8691..c9979385 100644 > > > --- a/src/cam/options.cpp > > > +++ b/src/cam/options.cpp > > > @@ -7,9 +7,11 @@ > > > > > > #include <assert.h> > > > #include <getopt.h> > > > -#include <iomanip> > > > -#include <iostream> > > > #include <string.h> > > > +#include <fmt/core.h> > > > + > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > > > #include "options.h" > > > > > > @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const > > > char *arguments) > > > continue; > > > > > > if (optionsMap_.find(key) == optionsMap_.end()) { > > > - std::cerr << "Invalid option " << key << std::endl; > > > + EPR("Invalid option {}\n", key); > > > return options; > > > } > > > > > > OptionArgument arg = optionsMap_[key].argument; > > > if (value.empty() && arg == ArgumentRequired) { > > > - std::cerr << "Option " << key << " requires an > > > argument" > > > - << std::endl; > > > + EPR("Option {} requires an argument\n", key); > > > return options; > > > } else if (!value.empty() && arg == ArgumentNone) { > > > - std::cerr << "Option " << key << " takes no > > > argument" > > > - << std::endl; > > > + EPR("Option {} takes no argument\n", key); > > > return options; > > > } > > > > > > const Option &option = optionsMap_[key]; > > > if (!options.parseValue(key, option, value.c_str())) { > > > - std::cerr << "Failed to parse '" << value << "' as > > > " > > > - << option.typeName() << " for option " > > > << key > > > - << std::endl; > > > + EPR("Failed to parse '{}' as {} for option {}\n", > > > + value, option.typeName(), key); > > > return options; > > > } > > > } > > > @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent) > > > argument += "]"; > > > } > > > > > > - std::cerr << std::setw(indent) << argument; > > > + EPR("{:{}}", argument, indent); > > > > > > for (const char *help = option.help, *end = help; end;) { > > > end = strchr(help, '\n'); > > > if (end) { > > > - std::cerr << std::string(help, end - help > > > + 1); > > > - std::cerr << std::setw(indent) << " "; > > > + EPR(std::string(help, end - help + 1)); > > > + EPR("{:{}}", "", indent); > > > help = end + 1; > > > } else { > > > - std::cerr << help << std::endl; > > > + EPR("{}\n", help); > > > } > > > } > > > } > > > @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int > > > argc, char **argv) > > > > > > if (c == '?' || c == ':') { > > > if (c == '?') > > > - std::cerr << "Invalid option "; > > > + EPR("Invalid option "); > > > else > > > - std::cerr << "Missing argument for option > > > "; > > > - std::cerr << argv[optind - 1] << std::endl; > > > + EPR("Missing argument for option "); > > > + EPR("{}\n", argv[optind - 1]); > > > > > > usage(); > > > return options; > > > @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc, > > > char **argv) > > > } > > > > > > if (optind < argc) { > > > - std::cerr << "Invalid non-option argument '" << > > > argv[optind] > > > - << "'" << std::endl; > > > + EPR("Invalid non-option argument '{}'\n", argv[optind]); > > > usage(); > > > return options; > > > } > > > @@ -992,14 +990,9 @@ void OptionsParser::usage() > > > > > > indent = (indent + 7) / 8 * 8; > > > > > > - std::cerr << "Options:" << std::endl; > > > - > > > - std::ios_base::fmtflags f(std::cerr.flags()); > > > - std::cerr << std::left; > > > + EPR("Options:\n"); > > > > > > usageOptions(options_, indent); > > > - > > > - std::cerr.flags(f); > > > } > > > > > > void OptionsParser::usageOptions(const std::list<Option> &options, > > > @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const > > > std::list<Option> &options, > > > if (option.isArray) > > > argument += " ..."; > > > > > > - std::cerr << std::setw(indent) << argument; > > > + EPR("{:{}}", argument, indent); > > > > > > - for (const char *help = option.help, *end = help; end; ) { > > > + for (const char *help = option.help, *end = help; end;) { > > > end = strchr(help, '\n'); > > > if (end) { > > > - std::cerr << std::string(help, end - help > > > + 1); > > > - std::cerr << std::setw(indent) << " "; > > > + EPR(std::string(help, end - help + 1)); > > > + EPR("{:{}}", "", indent); > > > help = end + 1; > > > } else { > > > - std::cerr << help << std::endl; > > > + EPR("{}\n", help); > > > } > > > } > > > > > > @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const > > > std::list<Option> &options, > > > return; > > > > > > for (const Option *option : parentOptions) { > > > - std::cerr << std::endl << "Options valid in the context of > > > " > > > - << option->optionName() << ":" << std::endl; > > > + EPR("\nOptions valid in the context of {}:\n", > > > + option->optionName()); > > > usageOptions(option->children, indent); > > > } > > > } > > > @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option > > > &option, const char *arg, > > > > > > std::tie(options, error) = childOption(option.parent, options); > > > if (error) { > > > - std::cerr << "Option " << option.optionName() << " > > > requires a " > > > - << error->optionName() << " context" << > > > std::endl; > > > + EPR("Option {} requires a {} context\n", > > > + option.optionName(), error->optionName()); > > > return false; > > > } > > > > > > if (!options->parseValue(option.opt, option, arg)) { > > > - std::cerr << "Can't parse " << option.typeName() > > > - << " argument for option " << option.optionName() > > > - << std::endl; > > > + EPR("Can't parse {} argument for option {}\n", > > > + option.typeName(), option.optionName()); > > > return false; > > > } > > > > > > diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp > > > index 150bd27c..666862eb 100644 > > > --- a/src/cam/stream_options.cpp > > > +++ b/src/cam/stream_options.cpp > > > @@ -6,7 +6,10 @@ > > > */ > > > #include "stream_options.h" > > > > > > -#include <iostream> > > > +#include <fmt/core.h> > > > + > > > +#define PR(...) fmt::print(__VA_ARGS__) > > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__) > > > > > > using namespace libcamera; > > > > > > @@ -30,8 +33,7 @@ KeyValueParser::Options > > > StreamKeyValueParser::parse(const char *arguments) > > > > > > if (options.valid() && options.isSet("role") && > > > !parseRole(&role, options)) { > > > - std::cerr << "Unknown stream role " > > > - << options["role"].toString() << std::endl; > > > + EPR("Unknown stream role {}\n", > > > options["role"].toString()); > > > options.invalidate(); > > > } > > > > > > @@ -64,7 +66,7 @@ int > > > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, > > > const OptionValue &values) > > > { > > > if (!config) { > > > - std::cerr << "No configuration provided" << std::endl; > > > + EPR("No configuration provided\n"); > > > return -EINVAL; > > > } > > > > > > @@ -75,12 +77,8 @@ int > > > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, > > > const std::vector<OptionValue> &streamParameters = > > > values.toArray(); > > > > > > if (config->size() != streamParameters.size()) { > > > - std::cerr > > > - << "Number of streams in configuration " > > > - << config->size() > > > - << " does not match number of streams parsed " > > > - << streamParameters.size() > > > - << std::endl; > > > + EPR("Number of streams in configuration {} does not match > > > number of streams parsed {}\n", > > > + config->size(), streamParameters.size()); > > > return -EINVAL; > > > } > > > > > > -- > > > 2.34.1 > > > > > > >
Hi Eric, On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > > Quoting Naushir Patuck (2022-05-10 10:49:14) > > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > > > > > > > This is just a conversation starter, not for merging. I really like > > > > libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > > is the first library I add to any new C++ project I make. > > > > > > > > You can find more information about libfmt from: > > > > > > > > https://github.com/fmtlib/fmt > > > > https://fmt.dev/latest/index.html > > > > > > > > This patch is just a crude conversion with ugly macros to showcase what > > > > the formatting code might look like. > > > > > > > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > > than iostreams, but for the size it didn't seem to be true in cam's case > > > > as the tests below show. However, simple prints did reduce the exe size, > > > > but the few more complex ones increased the size. > > > > > > > > Size tests with gcc 11.2.0-19ubuntu1 > > > > > > > > - Without libfmt > > > > > > > > debug 3523400 > > > > debug lto 3269368 > > > > release 223056 > > > > release lto 172280 > > > > > > > > - With libfmt > > > > > > > > debug 4424256 > > > > debug lto 4143840 > > > > release 303952 > > > > release lto 252640 > > > > > > > > Above shows that cam's size clearly increases with libfmt. However, the > > > > increase really comes only from one case, the use of fmt::memory_buffer > > > > and std::back_inserter. Converting that code to use fmt::format() and > > > > naive string append gives: > > > > > > > > release with string append 233680 > > > > release lto with string append 186936 > > > > > > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter > > > > to another file, I see much less increase in the size: > > > > > > > > release lto with two uses of memory_buffer, back_inserter 256736 > > > > > > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > > > > > > For what it's worth, I absolutely loathe formatting in std iostream, and > > > libfmt is a wonderful alternative. > > > It also is close enough to the C++20 std::format implementation that > > > eventual porting would be low effort. So I am all for this change :) It's going to take a while before we can switch to C++20, but I'm looking forward to it (and before that to some of the C++17 features too, it would be nice to use std::optional in the public API). > I am all in for this change also, personally I would have changed to > printf for now > to have one less dependency (also an easy port to C++20 std::format). The > dependency list is getting large, I noticed a build of mine failing > recently because > I didn't have libyaml. I've posted a patch that falls back to a subproject in that case. Would you be able to give it a try ? > But std::format and libfmt are quite fast and anything is better than > streams so +1. > > > I've never used (yet) {fmt}, but I've only heard good things about > > performance, and of course it's headed into the standard, so I also > > think there is some good merit to be found in this development.
Hi Tomi, On Tue, May 10, 2022 at 12:10:21PM +0300, Tomi Valkeinen wrote: > On 10/05/2022 10:16, Tomi Valkeinen wrote: > > This is just a conversation starter, not for merging. I really like > > libfmt, and I really hate the C++ stream-based string formatting. libfmt > > is the first library I add to any new C++ project I make. > > > > You can find more information about libfmt from: > > > > https://github.com/fmtlib/fmt > > https://fmt.dev/latest/index.html > > > > This patch is just a crude conversion with ugly macros to showcase what > > the formatting code might look like. > > As for the PR and EPR macros, those was just something to get forward > with. I think fmt::print("") is fine, but fmt::print(stderr, "") is a > bit long. > > Compared to cout, "fmt::print(" is actually shorter than "std::cout << > ", and without all those << and std::endl the lines are shorter. > > Not so with cerr and fmt::print(stderr, (although in many cases the > total length would still be shorter) but I think it makes sense to have > a macro/inline func for error prints, if only to make it more obvious > that it's an error print. > > And, of course, libfmt could also be used for logging: > > LOG(Camera, Error) << "Camera in " << camera_state_names[currentState] > << " state trying " << from << "() requiring state " > << camera_state_names[state]; > > to > > LOG(Camera, Error, "Camera in {} state trying {}() requiring state {}", > camera_state_names[currentState], from, camera_state_names[state]); That's an interesting example, it clearly shows one of the main semantic differences between the two options. std::format makes it easier to see the whole message as it's stored in a single string, but the drawback is that you have to go back and forth between the format string and the arguments to figure out what is getting printed. std::ostream makes the reading flow more natural in my opinion. I'm pretty sure my preference will vary based on the logging statement, some will become more readable, others will get worse. That won't help making a decision :-) It would be nice, however, to see how it would affect logging, with a sample patch for the logger. It should ideally also add a custom formatter for at least one of the geometry classes. This being said, we don't have to switch to libfmt (assuming we want to do so) everywhere, or everywhere at the same time. The cam application and the unit tests could possibly go first, as they make heavy use of std::cout and std::cerr (for the unit tests it would actually be nice to have a better form of status reporting, std::cerr may be best replaced with assertions that raise exceptions).
Hi Tomi, Thank you for the patch. On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > This is just a conversation starter, not for merging. I really like > libfmt, and I really hate the C++ stream-based string formatting. libfmt > is the first library I add to any new C++ project I make. You've got it wrong. The first library you should add to any new C++ project you make is libcamera :-) > You can find more information about libfmt from: > > https://github.com/fmtlib/fmt > https://fmt.dev/latest/index.html > > This patch is just a crude conversion with ugly macros to showcase what > the formatting code might look like. > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > than iostreams, but for the size it didn't seem to be true in cam's case > as the tests below show. However, simple prints did reduce the exe size, > but the few more complex ones increased the size. > > Size tests with gcc 11.2.0-19ubuntu1 > > - Without libfmt > > debug 3523400 > debug lto 3269368 > release 223056 > release lto 172280 > > - With libfmt > > debug 4424256 > debug lto 4143840 > release 303952 > release lto 252640 > > Above shows that cam's size clearly increases with libfmt. However, the > increase really comes only from one case, the use of fmt::memory_buffer > and std::back_inserter. Is this because libfmt is a header-only library ? What if it is built as a shared object, what's the impact on code size in cam ? > Converting that code to use fmt::format() and > naive string append gives: > > release with string append 233680 > release lto with string append 186936 What's the impact of switching to string concatenation on runtime ? Would you consider the option of keeping std::stringstream ? > Also, if I add another use of fmt::memory_buffer and std::back_inserter > to another file, I see much less increase in the size: > > release lto with two uses of memory_buffer, back_inserter 256736 > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > --- > src/cam/camera_session.cpp | 105 ++++++++++++++++--------------------- > src/cam/drm.cpp | 68 ++++++++---------------- > src/cam/event_loop.cpp | 9 ++-- > src/cam/file_sink.cpp | 31 +++++------ > src/cam/image.cpp | 15 +++--- > src/cam/kms_sink.cpp | 38 ++++++-------- > src/cam/main.cpp | 28 +++++----- > src/cam/meson.build | 5 +- > src/cam/options.cpp | 66 ++++++++++------------- > src/cam/stream_options.cpp | 18 +++---- > 10 files changed, 165 insertions(+), 218 deletions(-) [snip]
On Tue, May 10, 2022 at 04:22:51PM +0300, Laurent Pinchart wrote: > Hi Tomi, > > Thank you for the patch. > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > > This is just a conversation starter, not for merging. I really like > > libfmt, and I really hate the C++ stream-based string formatting. libfmt > > is the first library I add to any new C++ project I make. > > You've got it wrong. The first library you should add to any new C++ > project you make is libcamera :-) > > > You can find more information about libfmt from: > > > > https://github.com/fmtlib/fmt > > https://fmt.dev/latest/index.html > > > > This patch is just a crude conversion with ugly macros to showcase what > > the formatting code might look like. > > > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > > than iostreams, but for the size it didn't seem to be true in cam's case > > as the tests below show. However, simple prints did reduce the exe size, > > but the few more complex ones increased the size. > > > > Size tests with gcc 11.2.0-19ubuntu1 > > > > - Without libfmt > > > > debug 3523400 > > debug lto 3269368 > > release 223056 > > release lto 172280 > > > > - With libfmt > > > > debug 4424256 > > debug lto 4143840 > > release 303952 > > release lto 252640 > > > > Above shows that cam's size clearly increases with libfmt. However, the > > increase really comes only from one case, the use of fmt::memory_buffer > > and std::back_inserter. > > Is this because libfmt is a header-only library ? What if it is built as > a shared object, what's the impact on code size in cam ? It would also be useful to make the same measurements using the C++20 std::format, as that would be the long term goal. > > Converting that code to use fmt::format() and > > naive string append gives: > > > > release with string append 233680 > > release lto with string append 186936 > > What's the impact of switching to string concatenation on runtime ? > > Would you consider the option of keeping std::stringstream ? > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter > > to another file, I see much less increase in the size: > > > > release lto with two uses of memory_buffer, back_inserter 256736 > > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > > --- > > src/cam/camera_session.cpp | 105 ++++++++++++++++--------------------- > > src/cam/drm.cpp | 68 ++++++++---------------- > > src/cam/event_loop.cpp | 9 ++-- > > src/cam/file_sink.cpp | 31 +++++------ > > src/cam/image.cpp | 15 +++--- > > src/cam/kms_sink.cpp | 38 ++++++-------- > > src/cam/main.cpp | 28 +++++----- > > src/cam/meson.build | 5 +- > > src/cam/options.cpp | 66 ++++++++++------------- > > src/cam/stream_options.cpp | 18 +++---- > > 10 files changed, 165 insertions(+), 218 deletions(-) > > [snip]
On 10/05/2022 16:13, Laurent Pinchart wrote: > Hi Tomi, > > On Tue, May 10, 2022 at 12:10:21PM +0300, Tomi Valkeinen wrote: >> On 10/05/2022 10:16, Tomi Valkeinen wrote: >>> This is just a conversation starter, not for merging. I really like >>> libfmt, and I really hate the C++ stream-based string formatting. libfmt >>> is the first library I add to any new C++ project I make. >>> >>> You can find more information about libfmt from: >>> >>> https://github.com/fmtlib/fmt >>> https://fmt.dev/latest/index.html >>> >>> This patch is just a crude conversion with ugly macros to showcase what >>> the formatting code might look like. >> >> As for the PR and EPR macros, those was just something to get forward >> with. I think fmt::print("") is fine, but fmt::print(stderr, "") is a >> bit long. >> >> Compared to cout, "fmt::print(" is actually shorter than "std::cout << >> ", and without all those << and std::endl the lines are shorter. >> >> Not so with cerr and fmt::print(stderr, (although in many cases the >> total length would still be shorter) but I think it makes sense to have >> a macro/inline func for error prints, if only to make it more obvious >> that it's an error print. >> >> And, of course, libfmt could also be used for logging: >> >> LOG(Camera, Error) << "Camera in " << camera_state_names[currentState] >> << " state trying " << from << "() requiring state " >> << camera_state_names[state]; >> >> to >> >> LOG(Camera, Error, "Camera in {} state trying {}() requiring state {}", >> camera_state_names[currentState], from, camera_state_names[state]); > > That's an interesting example, it clearly shows one of the main semantic > differences between the two options. std::format makes it easier to see > the whole message as it's stored in a single string, but the drawback is > that you have to go back and forth between the format string and the > arguments to figure out what is getting printed. std::ostream makes the > reading flow more natural in my opinion. I think the bigger difference comes apparent when using more complex formats than just plain {} (i.e. setting precision, width, alignment, etc). > I'm pretty sure my preference will vary based on the logging statement, > some will become more readable, others will get worse. That won't help > making a decision :-) It would be nice, however, to see how it would Maybe there's a logging statement out there which is more readable with streams, but I'm not aware of it ;). > affect logging, with a sample patch for the logger. It should ideally > also add a custom formatter for at least one of the geometry classes. We already have that in this patch, as camera_session.cpp prints a Size. But it uses the existing std::ostream formatter from geometry.cpp, so... it's a bit boring =). libfmt has its own formatter support, which is useful if you want to pass formatting parameters to the formatter. Say, {:#x} and {:.2f} could be used to print "(0x4, 0x7)" and "(4.00, 7.00)" for the same Size. Tomi
Hi Tomi, On Tue, May 10, 2022 at 04:47:56PM +0300, Tomi Valkeinen wrote: > On 10/05/2022 16:13, Laurent Pinchart wrote: > > On Tue, May 10, 2022 at 12:10:21PM +0300, Tomi Valkeinen wrote: > >> On 10/05/2022 10:16, Tomi Valkeinen wrote: > >>> This is just a conversation starter, not for merging. I really like > >>> libfmt, and I really hate the C++ stream-based string formatting. libfmt > >>> is the first library I add to any new C++ project I make. > >>> > >>> You can find more information about libfmt from: > >>> > >>> https://github.com/fmtlib/fmt > >>> https://fmt.dev/latest/index.html > >>> > >>> This patch is just a crude conversion with ugly macros to showcase what > >>> the formatting code might look like. > >> > >> As for the PR and EPR macros, those was just something to get forward > >> with. I think fmt::print("") is fine, but fmt::print(stderr, "") is a > >> bit long. > >> > >> Compared to cout, "fmt::print(" is actually shorter than "std::cout << > >> ", and without all those << and std::endl the lines are shorter. > >> > >> Not so with cerr and fmt::print(stderr, (although in many cases the > >> total length would still be shorter) but I think it makes sense to have > >> a macro/inline func for error prints, if only to make it more obvious > >> that it's an error print. > >> > >> And, of course, libfmt could also be used for logging: > >> > >> LOG(Camera, Error) << "Camera in " << camera_state_names[currentState] > >> << " state trying " << from << "() requiring state " > >> << camera_state_names[state]; > >> > >> to > >> > >> LOG(Camera, Error, "Camera in {} state trying {}() requiring state {}", > >> camera_state_names[currentState], from, camera_state_names[state]); > > > > That's an interesting example, it clearly shows one of the main semantic > > differences between the two options. std::format makes it easier to see > > the whole message as it's stored in a single string, but the drawback is > > that you have to go back and forth between the format string and the > > arguments to figure out what is getting printed. std::ostream makes the > > reading flow more natural in my opinion. > > I think the bigger difference comes apparent when using more complex > formats than just plain {} (i.e. setting precision, width, alignment, etc). Once you get used to how the precision and other parameters are expressed with libfmt, it's indeed more concise, possibly easier to read, and certainly easier to write. > > I'm pretty sure my preference will vary based on the logging statement, > > some will become more readable, others will get worse. That won't help > > making a decision :-) It would be nice, however, to see how it would > > Maybe there's a logging statement out there which is more readable with > streams, but I'm not aware of it ;). I think the above example is more readable with streams :-) > > affect logging, with a sample patch for the logger. It should ideally > > also add a custom formatter for at least one of the geometry classes. > > We already have that in this patch, as camera_session.cpp prints a Size. > But it uses the existing std::ostream formatter from geometry.cpp, so... > it's a bit boring =). > > libfmt has its own formatter support, which is useful if you want to > pass formatting parameters to the formatter. Say, {:#x} and {:.2f} could > be used to print "(0x4, 0x7)" and "(4.00, 7.00)" for the same Size. I meant using custom libfmt formatters, yes, the same way we have custom operator<<() for the geometry classes. I'd like to see how that would look like.
On 10/05/2022 16:22, Laurent Pinchart wrote: > Hi Tomi, > > Thank you for the patch. > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: >> This is just a conversation starter, not for merging. I really like >> libfmt, and I really hate the C++ stream-based string formatting. libfmt >> is the first library I add to any new C++ project I make. > > You've got it wrong. The first library you should add to any new C++ > project you make is libcamera :-) > >> You can find more information about libfmt from: >> >> https://github.com/fmtlib/fmt >> https://fmt.dev/latest/index.html >> >> This patch is just a crude conversion with ugly macros to showcase what >> the formatting code might look like. >> >> libfmt pages suggest that libfmt would be faster and smaller (exe size) >> than iostreams, but for the size it didn't seem to be true in cam's case >> as the tests below show. However, simple prints did reduce the exe size, >> but the few more complex ones increased the size. >> >> Size tests with gcc 11.2.0-19ubuntu1 >> >> - Without libfmt >> >> debug 3523400 >> debug lto 3269368 >> release 223056 >> release lto 172280 >> >> - With libfmt >> >> debug 4424256 >> debug lto 4143840 >> release 303952 >> release lto 252640 >> >> Above shows that cam's size clearly increases with libfmt. However, the >> increase really comes only from one case, the use of fmt::memory_buffer >> and std::back_inserter. > > Is this because libfmt is a header-only library ? What if it is built as > a shared object, what's the impact on code size in cam ? libfmt supports header-only, but it's not header-only here. >> Converting that code to use fmt::format() and >> naive string append gives: >> >> release with string append 233680 >> release lto with string append 186936 > > What's the impact of switching to string concatenation on runtime ? What do you mean? > Would you consider the option of keeping std::stringstream ? You mean formatting mostly with libfmt, but sometimes with streams? Or using libfmt but appending the formatted string to stringstream? Tomi
Hi Tomi, On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: > On 10/05/2022 16:22, Laurent Pinchart wrote: > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > >> This is just a conversation starter, not for merging. I really like > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt > >> is the first library I add to any new C++ project I make. > > > > You've got it wrong. The first library you should add to any new C++ > > project you make is libcamera :-) > > > >> You can find more information about libfmt from: > >> > >> https://github.com/fmtlib/fmt > >> https://fmt.dev/latest/index.html > >> > >> This patch is just a crude conversion with ugly macros to showcase what > >> the formatting code might look like. > >> > >> libfmt pages suggest that libfmt would be faster and smaller (exe size) > >> than iostreams, but for the size it didn't seem to be true in cam's case > >> as the tests below show. However, simple prints did reduce the exe size, > >> but the few more complex ones increased the size. > >> > >> Size tests with gcc 11.2.0-19ubuntu1 > >> > >> - Without libfmt > >> > >> debug 3523400 > >> debug lto 3269368 > >> release 223056 > >> release lto 172280 > >> > >> - With libfmt > >> > >> debug 4424256 > >> debug lto 4143840 > >> release 303952 > >> release lto 252640 > >> > >> Above shows that cam's size clearly increases with libfmt. However, the > >> increase really comes only from one case, the use of fmt::memory_buffer > >> and std::back_inserter. > > > > Is this because libfmt is a header-only library ? What if it is built as > > a shared object, what's the impact on code size in cam ? > > libfmt supports header-only, but it's not header-only here. > > >> Converting that code to use fmt::format() and > >> naive string append gives: > >> > >> release with string append 233680 > >> release lto with string append 186936 > > > > What's the impact of switching to string concatenation on runtime ? > > What do you mean? I mean what is the impact on CPU usage of using the "naive" string append, compared to std::stringstream or fmt::memory_buffer ? > > Would you consider the option of keeping std::stringstream ? > > You mean formatting mostly with libfmt, but sometimes with streams? Or > using libfmt but appending the formatted string to stringstream? Mostly using fmt::, but using std::stringstream without fmt::memory_buffer when a string needs to be assembled. I'm not saying it's necessarily a good idea, just wondering.
On Tue, 10 May 2022 at 15:21, Laurent Pinchart via libcamera-devel <libcamera-devel@lists.libcamera.org> wrote: > > Hi Tomi, > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: > > On 10/05/2022 16:22, Laurent Pinchart wrote: > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > > >> This is just a conversation starter, not for merging. I really like > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt > > >> is the first library I add to any new C++ project I make. > > > > > > You've got it wrong. The first library you should add to any new C++ > > > project you make is libcamera :-) > > > > > >> You can find more information about libfmt from: > > >> > > >> https://github.com/fmtlib/fmt > > >> https://fmt.dev/latest/index.html > > >> > > >> This patch is just a crude conversion with ugly macros to showcase what > > >> the formatting code might look like. > > >> > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size) > > >> than iostreams, but for the size it didn't seem to be true in cam's case > > >> as the tests below show. However, simple prints did reduce the exe size, > > >> but the few more complex ones increased the size. > > >> > > >> Size tests with gcc 11.2.0-19ubuntu1 > > >> > > >> - Without libfmt > > >> > > >> debug 3523400 > > >> debug lto 3269368 > > >> release 223056 > > >> release lto 172280 > > >> > > >> - With libfmt > > >> > > >> debug 4424256 > > >> debug lto 4143840 > > >> release 303952 > > >> release lto 252640 > > >> > > >> Above shows that cam's size clearly increases with libfmt. However, the > > >> increase really comes only from one case, the use of fmt::memory_buffer > > >> and std::back_inserter. > > > > > > Is this because libfmt is a header-only library ? What if it is built as > > > a shared object, what's the impact on code size in cam ? > > > > libfmt supports header-only, but it's not header-only here. > > > > >> Converting that code to use fmt::format() and > > >> naive string append gives: > > >> > > >> release with string append 233680 > > >> release lto with string append 186936 > > > > > > What's the impact of switching to string concatenation on runtime ? > > > > What do you mean? > > I mean what is the impact on CPU usage of using the "naive" string > append, compared to std::stringstream or fmt::memory_buffer ? > > > > Would you consider the option of keeping std::stringstream ? > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or > > using libfmt but appending the formatted string to stringstream? > > Mostly using fmt::, but using std::stringstream without > fmt::memory_buffer when a string needs to be assembled. I'm not saying > it's necessarily a good idea, just wondering. > > -- > Regards, > > Laurent Pinchart > Something I wrote which you can use to format C++ strings in a printf-style ``` #pragma once #include <string.h> #include <string> // function that will sprintf to a C++ string starting from std::string::size() // so if you want to completely overwrite a string or start at a specific point // use std::string::clear() or std::string::resize(). str is a std::string. #define STRING_PRINTF(str, ...) \ do { \ const int size = snprintf(NULL, 0, __VA_ARGS__); \ const size_t start_of_string = str.size(); \ str.resize(start_of_string + size); \ snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \ } while (0) ``` This way you can format C++ strings in a sprintf-style. It would give you many of the benefits of fmt with just a few lines of code alternatively. I know we don't only use macros, but it makes sense here because when the compiler see's the snprintf, it warns against incorrect format strings when you do it this way. Just an option...
Hi Eric, On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote: > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote: > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: > > > On 10/05/2022 16:22, Laurent Pinchart wrote: > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > > > >> This is just a conversation starter, not for merging. I really like > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > >> is the first library I add to any new C++ project I make. > > > > > > > > You've got it wrong. The first library you should add to any new C++ > > > > project you make is libcamera :-) > > > > > > > >> You can find more information about libfmt from: > > > >> > > > >> https://github.com/fmtlib/fmt > > > >> https://fmt.dev/latest/index.html > > > >> > > > >> This patch is just a crude conversion with ugly macros to showcase what > > > >> the formatting code might look like. > > > >> > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > >> than iostreams, but for the size it didn't seem to be true in cam's case > > > >> as the tests below show. However, simple prints did reduce the exe size, > > > >> but the few more complex ones increased the size. > > > >> > > > >> Size tests with gcc 11.2.0-19ubuntu1 > > > >> > > > >> - Without libfmt > > > >> > > > >> debug 3523400 > > > >> debug lto 3269368 > > > >> release 223056 > > > >> release lto 172280 > > > >> > > > >> - With libfmt > > > >> > > > >> debug 4424256 > > > >> debug lto 4143840 > > > >> release 303952 > > > >> release lto 252640 > > > >> > > > >> Above shows that cam's size clearly increases with libfmt. However, the > > > >> increase really comes only from one case, the use of fmt::memory_buffer > > > >> and std::back_inserter. > > > > > > > > Is this because libfmt is a header-only library ? What if it is built as > > > > a shared object, what's the impact on code size in cam ? > > > > > > libfmt supports header-only, but it's not header-only here. > > > > > > >> Converting that code to use fmt::format() and > > > >> naive string append gives: > > > >> > > > >> release with string append 233680 > > > >> release lto with string append 186936 > > > > > > > > What's the impact of switching to string concatenation on runtime ? > > > > > > What do you mean? > > > > I mean what is the impact on CPU usage of using the "naive" string > > append, compared to std::stringstream or fmt::memory_buffer ? > > > > > > Would you consider the option of keeping std::stringstream ? > > > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or > > > using libfmt but appending the formatted string to stringstream? > > > > Mostly using fmt::, but using std::stringstream without > > fmt::memory_buffer when a string needs to be assembled. I'm not saying > > it's necessarily a good idea, just wondering. > > Something I wrote which you can use to format C++ strings in a printf-style > > ``` > #pragma once > > #include <string.h> > #include <string> > > // function that will sprintf to a C++ string starting from std::string::size() > // so if you want to completely overwrite a string or start at a specific point > // use std::string::clear() or std::string::resize(). str is a std::string. > #define STRING_PRINTF(str, ...) \ > do { \ > const int size = snprintf(NULL, 0, __VA_ARGS__); \ > const size_t start_of_string = str.size(); \ > str.resize(start_of_string + size); \ > snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \ > } while (0) > ``` > > This way you can format C++ strings in a sprintf-style. It would give > you many of the benefits of fmt with just a few lines of code > alternatively. I know we don't only use macros, but it makes sense > here because when the compiler see's the snprintf, it warns against > incorrect format strings when you do it this way. Beside the fact that snprintf() is called twice which may be less efficient (although maybe still more efficient than libfmt), the trouble here is that __VA_ARGS__ will be evaluated twice. unsigned int index; std::string str; index = 0; STRING_PRINTF(str, "%u", index++); /* Now index is equal to 2 */ > Just an option...
Quoting Laurent Pinchart (2022-05-10 14:04:57) > Hi Eric, > > On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > > On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > > > Quoting Naushir Patuck (2022-05-10 10:49:14) > > > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > > > > > > > > > This is just a conversation starter, not for merging. I really like > > > > > libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > > > is the first library I add to any new C++ project I make. > > > > > > > > > > You can find more information about libfmt from: > > > > > > > > > > https://github.com/fmtlib/fmt > > > > > https://fmt.dev/latest/index.html > > > > > > > > > > This patch is just a crude conversion with ugly macros to showcase what > > > > > the formatting code might look like. > > > > > > > > > > libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > > > than iostreams, but for the size it didn't seem to be true in cam's case > > > > > as the tests below show. However, simple prints did reduce the exe size, > > > > > but the few more complex ones increased the size. > > > > > > > > > > Size tests with gcc 11.2.0-19ubuntu1 > > > > > > > > > > - Without libfmt > > > > > > > > > > debug 3523400 > > > > > debug lto 3269368 > > > > > release 223056 > > > > > release lto 172280 > > > > > > > > > > - With libfmt > > > > > > > > > > debug 4424256 > > > > > debug lto 4143840 > > > > > release 303952 > > > > > release lto 252640 > > > > > > > > > > Above shows that cam's size clearly increases with libfmt. However, the > > > > > increase really comes only from one case, the use of fmt::memory_buffer > > > > > and std::back_inserter. Converting that code to use fmt::format() and > > > > > naive string append gives: > > > > > > > > > > release with string append 233680 > > > > > release lto with string append 186936 > > > > > > > > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter > > > > > to another file, I see much less increase in the size: > > > > > > > > > > release lto with two uses of memory_buffer, back_inserter 256736 > > > > > > > > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > > > > > > > > For what it's worth, I absolutely loathe formatting in std iostream, and > > > > libfmt is a wonderful alternative. > > > > It also is close enough to the C++20 std::format implementation that > > > > eventual porting would be low effort. So I am all for this change :) > > It's going to take a while before we can switch to C++20, but I'm > looking forward to it (and before that to some of the C++17 features > too, it would be nice to use std::optional in the public API). So, I've just seen as Christian said - we already do! It's used by color space .. So we're faced with either reverting, or rolling back on the color-space change, or pushing forwards with C++17 on the public-api. Maybe this deserves it's own thread... -- Kieran > > > I am all in for this change also, personally I would have changed to > > printf for now > > to have one less dependency (also an easy port to C++20 std::format). The > > dependency list is getting large, I noticed a build of mine failing > > recently because > > I didn't have libyaml. > > I've posted a patch that falls back to a subproject in that case. Would > you be able to give it a try ? > > > But std::format and libfmt are quite fast and anything is better than > > streams so +1. > > > > > I've never used (yet) {fmt}, but I've only heard good things about > > > performance, and of course it's headed into the standard, so I also > > > think there is some good merit to be found in this development. > > -- > Regards, > > Laurent Pinchart
On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: > Quoting Laurent Pinchart (2022-05-10 14:04:57) > > On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > > > On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > > > > Quoting Naushir Patuck (2022-05-10 10:49:14) > > > > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: [snip] > > > > > For what it's worth, I absolutely loathe formatting in std iostream, and > > > > > libfmt is a wonderful alternative. > > > > > It also is close enough to the C++20 std::format implementation that > > > > > eventual porting would be low effort. So I am all for this change :) > > > > It's going to take a while before we can switch to C++20, but I'm > > looking forward to it (and before that to some of the C++17 features > > too, it would be nice to use std::optional in the public API). > > > So, I've just seen as Christian said - we already do! > > It's used by color space .. Oops... > So we're faced with either reverting, or rolling back on the color-space > change, or pushing forwards with C++17 on the public-api. Switching to C++17 worries me as it's fairly recent and may not be easily available for all users. std::optional could easily rbe replaced with a custom implementation in utils::optional if needed. I'm tempted to leave std::optional in for now, and accept new users of std::optional but no other C++17 APIs. Switching to utils::optional for ColorSpace would cover new users of std::optional so we're not creating any new liability. > Maybe this deserves it's own thread... Done :-) > > > I am all in for this change also, personally I would have changed to > > > printf for now > > > to have one less dependency (also an easy port to C++20 std::format). The > > > dependency list is getting large, I noticed a build of mine failing > > > recently because > > > I didn't have libyaml. > > > > I've posted a patch that falls back to a subproject in that case. Would > > you be able to give it a try ? > > > > > But std::format and libfmt are quite fast and anything is better than > > > streams so +1. > > > > > > > I've never used (yet) {fmt}, but I've only heard good things about > > > > performance, and of course it's headed into the standard, so I also > > > > think there is some good merit to be found in this development.
On Sun, 22 May 2022 at 10:58, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote: > > Hi Eric, > > On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote: > > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote: > > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: > > > > On 10/05/2022 16:22, Laurent Pinchart wrote: > > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > > > > >> This is just a conversation starter, not for merging. I really like > > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > > >> is the first library I add to any new C++ project I make. > > > > > > > > > > You've got it wrong. The first library you should add to any new C++ > > > > > project you make is libcamera :-) > > > > > > > > > >> You can find more information about libfmt from: > > > > >> > > > > >> https://github.com/fmtlib/fmt > > > > >> https://fmt.dev/latest/index.html > > > > >> > > > > >> This patch is just a crude conversion with ugly macros to showcase what > > > > >> the formatting code might look like. > > > > >> > > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > > >> than iostreams, but for the size it didn't seem to be true in cam's case > > > > >> as the tests below show. However, simple prints did reduce the exe size, > > > > >> but the few more complex ones increased the size. > > > > >> > > > > >> Size tests with gcc 11.2.0-19ubuntu1 > > > > >> > > > > >> - Without libfmt > > > > >> > > > > >> debug 3523400 > > > > >> debug lto 3269368 > > > > >> release 223056 > > > > >> release lto 172280 > > > > >> > > > > >> - With libfmt > > > > >> > > > > >> debug 4424256 > > > > >> debug lto 4143840 > > > > >> release 303952 > > > > >> release lto 252640 > > > > >> > > > > >> Above shows that cam's size clearly increases with libfmt. However, the > > > > >> increase really comes only from one case, the use of fmt::memory_buffer > > > > >> and std::back_inserter. > > > > > > > > > > Is this because libfmt is a header-only library ? What if it is built as > > > > > a shared object, what's the impact on code size in cam ? > > > > > > > > libfmt supports header-only, but it's not header-only here. > > > > > > > > >> Converting that code to use fmt::format() and > > > > >> naive string append gives: > > > > >> > > > > >> release with string append 233680 > > > > >> release lto with string append 186936 > > > > > > > > > > What's the impact of switching to string concatenation on runtime ? > > > > > > > > What do you mean? > > > > > > I mean what is the impact on CPU usage of using the "naive" string > > > append, compared to std::stringstream or fmt::memory_buffer ? > > > > > > > > Would you consider the option of keeping std::stringstream ? > > > > > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or > > > > using libfmt but appending the formatted string to stringstream? > > > > > > Mostly using fmt::, but using std::stringstream without > > > fmt::memory_buffer when a string needs to be assembled. I'm not saying > > > it's necessarily a good idea, just wondering. > > > > Something I wrote which you can use to format C++ strings in a printf-style > > > > ``` > > #pragma once > > > > #include <string.h> > > #include <string> > > > > // function that will sprintf to a C++ string starting from std::string::size() > > // so if you want to completely overwrite a string or start at a specific point > > // use std::string::clear() or std::string::resize(). str is a std::string. > > #define STRING_PRINTF(str, ...) \ > > do { \ > > const int size = snprintf(NULL, 0, __VA_ARGS__); \ > > const size_t start_of_string = str.size(); \ > > str.resize(start_of_string + size); \ > > snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \ > > } while (0) > > ``` > > > > This way you can format C++ strings in a sprintf-style. It would give > > you many of the benefits of fmt with just a few lines of code > > alternatively. I know we don't only use macros, but it makes sense > > here because when the compiler see's the snprintf, it warns against > > incorrect format strings when you do it this way. > > Beside the fact that snprintf() is called twice which may be less > efficient (although maybe still more efficient than libfmt), the trouble > here is that __VA_ARGS__ will be evaluated twice. > > unsigned int index; > std::string str; > > index = 0; > STRING_PRINTF(str, "%u", index++); These simple cases could use: std::string str = std::to_string(index++); It's unlikely that you are gonna beat std::to_string for a simple conversion like this (well, libfmt beats everything in terms of execution speed but it's another dependency, I've done a few benchmarks in the past). STRING_PRINTF would easily convert to std::format in future. The solution is efficient in terms of memory but will do an evaluation twice as you said. The above macro was written with readability and future compatibility to std::format in mind, and is not very different to what folly from Meta does (only much simpler and compiler friendly), see stringPrintf https://github.com/facebook/folly/blob/main/folly/String.cpp STRING_PRINTF suits the case where you have a longer format string and you want to do something a bit more complex, so something like: std::stringstream info; info << ts / 1000000000 << "." << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 << " (" << std::fixed << std::setprecision(2) << fps << " fps)"; for (const auto &[stream, buffer] : buffers) { const FrameMetadata &metadata = buffer->metadata(); info << " " << streamNames_[stream] << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence << " bytesused: "; unsigned int nplane = 0; for (const FrameMetadata::Plane &plane : metadata.planes()) { info << plane.bytesused; if (++nplane < metadata.planes().size()) info << "/"; } } if (sink_) { if (!sink_->processRequest(request)) requeue = false; } std::cout << info.str() << std::endl; would become: std::string info STRING_PRINTF(info, "%.6f (%.2f fps)", ts / 1000000000.0, fps); for (const auto &[stream, buffer] : buffers) { const FrameMetadata &metadata = buffer->metadata(); STRING_PRINTF(info, " %s seq: %06d bytesused: ", streamNames_[buf.first].c_str(), metadata.sequence); unsigned int nplane = 0; for (const FrameMetadata::Plane &plane : metadata.planes()) { frame_str += std::to_string(plane.bytesused); if (++nplane < metadata.planes().size()) info += "/"; } } if (sink_) { if (!sink_->processRequest(request)) requeue = false; } printf("%s\n", info.c_str()); > /* Now index is equal to 2 */ > > > Just an option... > > -- > Regards, > > Laurent Pinchart >
Hi Eric, On Sun, May 22, 2022 at 09:00:04PM +0100, Eric Curtin wrote: > On Sun, 22 May 2022 at 10:58, Laurent Pinchart wrote: > > On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote: > > > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote: > > > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: > > > > > On 10/05/2022 16:22, Laurent Pinchart wrote: > > > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > > > > > >> This is just a conversation starter, not for merging. I really like > > > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > > > >> is the first library I add to any new C++ project I make. > > > > > > > > > > > > You've got it wrong. The first library you should add to any new C++ > > > > > > project you make is libcamera :-) > > > > > > > > > > > >> You can find more information about libfmt from: > > > > > >> > > > > > >> https://github.com/fmtlib/fmt > > > > > >> https://fmt.dev/latest/index.html > > > > > >> > > > > > >> This patch is just a crude conversion with ugly macros to showcase what > > > > > >> the formatting code might look like. > > > > > >> > > > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > > > >> than iostreams, but for the size it didn't seem to be true in cam's case > > > > > >> as the tests below show. However, simple prints did reduce the exe size, > > > > > >> but the few more complex ones increased the size. > > > > > >> > > > > > >> Size tests with gcc 11.2.0-19ubuntu1 > > > > > >> > > > > > >> - Without libfmt > > > > > >> > > > > > >> debug 3523400 > > > > > >> debug lto 3269368 > > > > > >> release 223056 > > > > > >> release lto 172280 > > > > > >> > > > > > >> - With libfmt > > > > > >> > > > > > >> debug 4424256 > > > > > >> debug lto 4143840 > > > > > >> release 303952 > > > > > >> release lto 252640 > > > > > >> > > > > > >> Above shows that cam's size clearly increases with libfmt. However, the > > > > > >> increase really comes only from one case, the use of fmt::memory_buffer > > > > > >> and std::back_inserter. > > > > > > > > > > > > Is this because libfmt is a header-only library ? What if it is built as > > > > > > a shared object, what's the impact on code size in cam ? > > > > > > > > > > libfmt supports header-only, but it's not header-only here. > > > > > > > > > > >> Converting that code to use fmt::format() and > > > > > >> naive string append gives: > > > > > >> > > > > > >> release with string append 233680 > > > > > >> release lto with string append 186936 > > > > > > > > > > > > What's the impact of switching to string concatenation on runtime ? > > > > > > > > > > What do you mean? > > > > > > > > I mean what is the impact on CPU usage of using the "naive" string > > > > append, compared to std::stringstream or fmt::memory_buffer ? > > > > > > > > > > Would you consider the option of keeping std::stringstream ? > > > > > > > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or > > > > > using libfmt but appending the formatted string to stringstream? > > > > > > > > Mostly using fmt::, but using std::stringstream without > > > > fmt::memory_buffer when a string needs to be assembled. I'm not saying > > > > it's necessarily a good idea, just wondering. > > > > > > Something I wrote which you can use to format C++ strings in a printf-style > > > > > > ``` > > > #pragma once > > > > > > #include <string.h> > > > #include <string> > > > > > > // function that will sprintf to a C++ string starting from std::string::size() > > > // so if you want to completely overwrite a string or start at a specific point > > > // use std::string::clear() or std::string::resize(). str is a std::string. > > > #define STRING_PRINTF(str, ...) \ > > > do { \ > > > const int size = snprintf(NULL, 0, __VA_ARGS__); \ > > > const size_t start_of_string = str.size(); \ > > > str.resize(start_of_string + size); \ > > > snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \ > > > } while (0) > > > ``` > > > > > > This way you can format C++ strings in a sprintf-style. It would give > > > you many of the benefits of fmt with just a few lines of code > > > alternatively. I know we don't only use macros, but it makes sense > > > here because when the compiler see's the snprintf, it warns against > > > incorrect format strings when you do it this way. > > > > Beside the fact that snprintf() is called twice which may be less > > efficient (although maybe still more efficient than libfmt), the trouble > > here is that __VA_ARGS__ will be evaluated twice. > > > > unsigned int index; > > std::string str; > > > > index = 0; > > STRING_PRINTF(str, "%u", index++); > > These simple cases could use: > > std::string str = std::to_string(index++); Of course. It was just an example of potential issues with multiple evaluation of macro arguments. It should be possible to turn the macro into a function instead, but I think we'll then end up reinventing the wheel. > It's unlikely that you are gonna beat std::to_string for a simple > conversion like this (well, libfmt beats everything in terms of > execution speed but it's another dependency, I've done a few > benchmarks in the past). STRING_PRINTF would easily convert to > std::format in future. The solution is efficient in terms of memory > but will do an evaluation twice as you said. The above macro was > written with readability and future compatibility to std::format in > mind, and is not very different to what folly from Meta does (only > much simpler and compiler friendly), see stringPrintf > > https://github.com/facebook/folly/blob/main/folly/String.cpp > > STRING_PRINTF suits the case where you have a longer format string and > you want to do something a bit more complex, so something like: > > std::stringstream info; > info << ts / 1000000000 << "." > << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 > << " (" << std::fixed << std::setprecision(2) << fps << " fps)"; > > for (const auto &[stream, buffer] : buffers) { > const FrameMetadata &metadata = buffer->metadata(); > > info << " " << streamNames_[stream] > << " seq: " << std::setw(6) << std::setfill('0') > << metadata.sequence > << " bytesused: "; > > unsigned int nplane = 0; > for (const FrameMetadata::Plane &plane : metadata.planes()) { > info << plane.bytesused; > if (++nplane < metadata.planes().size()) > info << "/"; > } > } > > if (sink_) { > if (!sink_->processRequest(request)) > requeue = false; > } > > std::cout << info.str() << std::endl; > > would become: > > std::string info > STRING_PRINTF(info, "%.6f (%.2f fps)", ts / 1000000000.0, fps); > > for (const auto &[stream, buffer] : buffers) { > const FrameMetadata &metadata = buffer->metadata(); > > STRING_PRINTF(info, > " %s seq: %06d bytesused: ", streamNames_[buf.first].c_str(), > metadata.sequence); > > unsigned int nplane = 0; > for (const FrameMetadata::Plane &plane : metadata.planes()) { > frame_str += std::to_string(plane.bytesused); > if (++nplane < metadata.planes().size()) > info += "/"; > } > } > > if (sink_) { > if (!sink_->processRequest(request)) > requeue = false; > } > > printf("%s\n", info.c_str()); > > > /* Now index is equal to 2 */ > > > > > Just an option...
On Mon, 23 May 2022 at 09:23, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote: > > Hi Eric, > > On Sun, May 22, 2022 at 09:00:04PM +0100, Eric Curtin wrote: > > On Sun, 22 May 2022 at 10:58, Laurent Pinchart wrote: > > > On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote: > > > > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote: > > > > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: > > > > > > On 10/05/2022 16:22, Laurent Pinchart wrote: > > > > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: > > > > > > >> This is just a conversation starter, not for merging. I really like > > > > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt > > > > > > >> is the first library I add to any new C++ project I make. > > > > > > > > > > > > > > You've got it wrong. The first library you should add to any new C++ > > > > > > > project you make is libcamera :-) > > > > > > > > > > > > > >> You can find more information about libfmt from: > > > > > > >> > > > > > > >> https://github.com/fmtlib/fmt > > > > > > >> https://fmt.dev/latest/index.html > > > > > > >> > > > > > > >> This patch is just a crude conversion with ugly macros to showcase what > > > > > > >> the formatting code might look like. > > > > > > >> > > > > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size) > > > > > > >> than iostreams, but for the size it didn't seem to be true in cam's case > > > > > > >> as the tests below show. However, simple prints did reduce the exe size, > > > > > > >> but the few more complex ones increased the size. > > > > > > >> > > > > > > >> Size tests with gcc 11.2.0-19ubuntu1 > > > > > > >> > > > > > > >> - Without libfmt > > > > > > >> > > > > > > >> debug 3523400 > > > > > > >> debug lto 3269368 > > > > > > >> release 223056 > > > > > > >> release lto 172280 > > > > > > >> > > > > > > >> - With libfmt > > > > > > >> > > > > > > >> debug 4424256 > > > > > > >> debug lto 4143840 > > > > > > >> release 303952 > > > > > > >> release lto 252640 > > > > > > >> > > > > > > >> Above shows that cam's size clearly increases with libfmt. However, the > > > > > > >> increase really comes only from one case, the use of fmt::memory_buffer > > > > > > >> and std::back_inserter. > > > > > > > > > > > > > > Is this because libfmt is a header-only library ? What if it is built as > > > > > > > a shared object, what's the impact on code size in cam ? > > > > > > > > > > > > libfmt supports header-only, but it's not header-only here. > > > > > > > > > > > > >> Converting that code to use fmt::format() and > > > > > > >> naive string append gives: > > > > > > >> > > > > > > >> release with string append 233680 > > > > > > >> release lto with string append 186936 > > > > > > > > > > > > > > What's the impact of switching to string concatenation on runtime ? > > > > > > > > > > > > What do you mean? > > > > > > > > > > I mean what is the impact on CPU usage of using the "naive" string > > > > > append, compared to std::stringstream or fmt::memory_buffer ? > > > > > > > > > > > > Would you consider the option of keeping std::stringstream ? > > > > > > > > > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or > > > > > > using libfmt but appending the formatted string to stringstream? > > > > > > > > > > Mostly using fmt::, but using std::stringstream without > > > > > fmt::memory_buffer when a string needs to be assembled. I'm not saying > > > > > it's necessarily a good idea, just wondering. > > > > > > > > Something I wrote which you can use to format C++ strings in a printf-style > > > > > > > > ``` > > > > #pragma once > > > > > > > > #include <string.h> > > > > #include <string> > > > > > > > > // function that will sprintf to a C++ string starting from std::string::size() > > > > // so if you want to completely overwrite a string or start at a specific point > > > > // use std::string::clear() or std::string::resize(). str is a std::string. > > > > #define STRING_PRINTF(str, ...) \ > > > > do { \ > > > > const int size = snprintf(NULL, 0, __VA_ARGS__); \ > > > > const size_t start_of_string = str.size(); \ > > > > str.resize(start_of_string + size); \ > > > > snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \ > > > > } while (0) > > > > ``` > > > > > > > > This way you can format C++ strings in a sprintf-style. It would give > > > > you many of the benefits of fmt with just a few lines of code > > > > alternatively. I know we don't only use macros, but it makes sense > > > > here because when the compiler see's the snprintf, it warns against > > > > incorrect format strings when you do it this way. > > > > > > Beside the fact that snprintf() is called twice which may be less > > > efficient (although maybe still more efficient than libfmt), the trouble > > > here is that __VA_ARGS__ will be evaluated twice. > > > > > > unsigned int index; > > > std::string str; > > > > > > index = 0; > > > STRING_PRINTF(str, "%u", index++); > > > > These simple cases could use: > > > > std::string str = std::to_string(index++); > > Of course. It was just an example of potential issues with multiple > evaluation of macro arguments. It should be possible to turn the macro > into a function instead, but I think we'll then end up reinventing the > wheel. Yeah I get that, I'm not exactly hell bent on changing this, just curiosity at this point (although admittedly I prefer printf or libfmt style, and it's generally faster). I did a crude benchmark here, just to solve curiosity: https://github.com/ericcurtin/staging/blob/master/string_format_bench.cpp $ g++ -O3 string_format_bench.cpp; ./a.out > /dev/null folly: 3.447348 seconds folly2: 3.343155 seconds string_printf: 7.059757 seconds string_printf2: 3.468643 seconds to_string: 4.890018 seconds ostringstream: 5.817828 seconds string_printf2 takes some influence from folly and the first time it parses, it optimistically guesses the output string could be less than 128 characters, which it probably is in many cases. I actually wrote folly2 as a further optimization and opened a pull request in Meta's folly, they missed a trick there by not writing directly into the string, they do an extra unnecessary copy. STRING_PRINTF2 is my favorite, but each to their own :) > > > It's unlikely that you are gonna beat std::to_string for a simple > > conversion like this (well, libfmt beats everything in terms of > > execution speed but it's another dependency, I've done a few > > benchmarks in the past). STRING_PRINTF would easily convert to > > std::format in future. The solution is efficient in terms of memory > > but will do an evaluation twice as you said. The above macro was > > written with readability and future compatibility to std::format in > > mind, and is not very different to what folly from Meta does (only > > much simpler and compiler friendly), see stringPrintf > > > > https://github.com/facebook/folly/blob/main/folly/String.cpp > > > > STRING_PRINTF suits the case where you have a longer format string and > > you want to do something a bit more complex, so something like: > > > > std::stringstream info; > > info << ts / 1000000000 << "." > > << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 > > << " (" << std::fixed << std::setprecision(2) << fps << " fps)"; > > > > for (const auto &[stream, buffer] : buffers) { > > const FrameMetadata &metadata = buffer->metadata(); > > > > info << " " << streamNames_[stream] > > << " seq: " << std::setw(6) << std::setfill('0') > > << metadata.sequence > > << " bytesused: "; > > > > unsigned int nplane = 0; > > for (const FrameMetadata::Plane &plane : metadata.planes()) { > > info << plane.bytesused; > > if (++nplane < metadata.planes().size()) > > info << "/"; > > } > > } > > > > if (sink_) { > > if (!sink_->processRequest(request)) > > requeue = false; > > } > > > > std::cout << info.str() << std::endl; > > > > would become: > > > > std::string info > > STRING_PRINTF(info, "%.6f (%.2f fps)", ts / 1000000000.0, fps); > > > > for (const auto &[stream, buffer] : buffers) { > > const FrameMetadata &metadata = buffer->metadata(); > > > > STRING_PRINTF(info, > > " %s seq: %06d bytesused: ", streamNames_[buf.first].c_str(), > > metadata.sequence); > > > > unsigned int nplane = 0; > > for (const FrameMetadata::Plane &plane : metadata.planes()) { > > frame_str += std::to_string(plane.bytesused); > > if (++nplane < metadata.planes().size()) > > info += "/"; > > } > > } > > > > if (sink_) { > > if (!sink_->processRequest(request)) > > requeue = false; > > } > > > > printf("%s\n", info.c_str()); > > > > > /* Now index is equal to 2 */ > > > > > > > Just an option... > > -- > Regards, > > Laurent Pinchart >
On 10/05/2022 17:20, Laurent Pinchart wrote: > Hi Tomi, > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote: >> On 10/05/2022 16:22, Laurent Pinchart wrote: >>> On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote: >>>> This is just a conversation starter, not for merging. I really like >>>> libfmt, and I really hate the C++ stream-based string formatting. libfmt >>>> is the first library I add to any new C++ project I make. >>> >>> You've got it wrong. The first library you should add to any new C++ >>> project you make is libcamera :-) >>> >>>> You can find more information about libfmt from: >>>> >>>> https://github.com/fmtlib/fmt >>>> https://fmt.dev/latest/index.html >>>> >>>> This patch is just a crude conversion with ugly macros to showcase what >>>> the formatting code might look like. >>>> >>>> libfmt pages suggest that libfmt would be faster and smaller (exe size) >>>> than iostreams, but for the size it didn't seem to be true in cam's case >>>> as the tests below show. However, simple prints did reduce the exe size, >>>> but the few more complex ones increased the size. >>>> >>>> Size tests with gcc 11.2.0-19ubuntu1 >>>> >>>> - Without libfmt >>>> >>>> debug 3523400 >>>> debug lto 3269368 >>>> release 223056 >>>> release lto 172280 >>>> >>>> - With libfmt >>>> >>>> debug 4424256 >>>> debug lto 4143840 >>>> release 303952 >>>> release lto 252640 >>>> >>>> Above shows that cam's size clearly increases with libfmt. However, the >>>> increase really comes only from one case, the use of fmt::memory_buffer >>>> and std::back_inserter. >>> >>> Is this because libfmt is a header-only library ? What if it is built as >>> a shared object, what's the impact on code size in cam ? >> >> libfmt supports header-only, but it's not header-only here. >> >>>> Converting that code to use fmt::format() and >>>> naive string append gives: >>>> >>>> release with string append 233680 >>>> release lto with string append 186936 >>> >>> What's the impact of switching to string concatenation on runtime ? >> >> What do you mean? > > I mean what is the impact on CPU usage of using the "naive" string > append, compared to std::stringstream or fmt::memory_buffer ? > >>> Would you consider the option of keeping std::stringstream ? >> >> You mean formatting mostly with libfmt, but sometimes with streams? Or >> using libfmt but appending the formatted string to stringstream? > > Mostly using fmt::, but using std::stringstream without > fmt::memory_buffer when a string needs to be assembled. I'm not saying > it's necessarily a good idea, just wondering. I did test using fmt::format() and assembling the result to std::stringstream, and that does keep the executable size down. So somehow fmt::format_to() seems to pull in a lot of code. I got a comment on libfmt gitter: "Normally format and format_to are compiled to the same underlying calls where the iterator is type erased.". I'm currently inclined to believe it's a bug. Tomi
Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: > On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: >> Quoting Laurent Pinchart (2022-05-10 14:04:57) >>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: >>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: >>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) >>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > > [snip] > >>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and >>>>>> libfmt is a wonderful alternative. >>>>>> It also is close enough to the C++20 std::format implementation that >>>>>> eventual porting would be low effort. So I am all for this change :) >>> >>> It's going to take a while before we can switch to C++20, but I'm >>> looking forward to it (and before that to some of the C++17 features >>> too, it would be nice to use std::optional in the public API). >> >> >> So, I've just seen as Christian said - we already do! >> >> It's used by color space .. > > Oops... > >> So we're faced with either reverting, or rolling back on the color-space >> change, or pushing forwards with C++17 on the public-api. > > Switching to C++17 worries me as it's fairly recent and may not be > easily available for all users. std::optional could easily rbe replaced > with a custom implementation in utils::optional if needed. I'm tempted > to leave std::optional in for now, and accept new users of std::optional > but no other C++17 APIs. Switching to utils::optional for ColorSpace > would cover new users of std::optional so we're not creating any new > liability. I think that C++17 is quite old by now (nearly 5 years) and all major compilers support it for multiple versions and years now. Do you know of an example, where requiring C++17 would hinder the adaptation of libcamera? Do any of the target system not support this standard? I think the use of std::optional to express invalid values is quite significant compared to the previous solution of using default constructed values. Reimplementing this in libcamera would be a solution, but it just adds development and maintenance costs for no obvious reason (to me). > >> Maybe this deserves it's own thread... > > Done :-) > >>>> I am all in for this change also, personally I would have changed to >>>> printf for now >>>> to have one less dependency (also an easy port to C++20 std::format). The >>>> dependency list is getting large, I noticed a build of mine failing >>>> recently because >>>> I didn't have libyaml. >>> >>> I've posted a patch that falls back to a subproject in that case. Would >>> you be able to give it a try ? >>> >>>> But std::format and libfmt are quite fast and anything is better than >>>> streams so +1. >>>> >>>>> I've never used (yet) {fmt}, but I've only heard good things about >>>>> performance, and of course it's headed into the standard, so I also >>>>> think there is some good merit to be found in this development. >
On Sat, 28 May 2022 at 00:19, Christian Rauch via libcamera-devel <libcamera-devel@lists.libcamera.org> wrote: > > > > Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: > > On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: > >> Quoting Laurent Pinchart (2022-05-10 14:04:57) > >>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > >>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > >>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) > >>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > > > > [snip] > > > >>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and > >>>>>> libfmt is a wonderful alternative. > >>>>>> It also is close enough to the C++20 std::format implementation that > >>>>>> eventual porting would be low effort. So I am all for this change :) > >>> > >>> It's going to take a while before we can switch to C++20, but I'm > >>> looking forward to it (and before that to some of the C++17 features > >>> too, it would be nice to use std::optional in the public API). > >> > >> > >> So, I've just seen as Christian said - we already do! > >> > >> It's used by color space .. > > > > Oops... > > > >> So we're faced with either reverting, or rolling back on the color-space > >> change, or pushing forwards with C++17 on the public-api. > > > > Switching to C++17 worries me as it's fairly recent and may not be > > easily available for all users. std::optional could easily rbe replaced > > with a custom implementation in utils::optional if needed. I'm tempted > > to leave std::optional in for now, and accept new users of std::optional > > but no other C++17 APIs. Switching to utils::optional for ColorSpace > > would cover new users of std::optional so we're not creating any new > > liability. > > I think that C++17 is quite old by now (nearly 5 years) and all major > compilers support it for multiple versions and years now. Do you know of > an example, where requiring C++17 would hinder the adaptation of > libcamera? Do any of the target system not support this standard? > > I think the use of std::optional to express invalid values is quite > significant compared to the previous solution of using default > constructed values. Reimplementing this in libcamera would be a > solution, but it just adds development and maintenance costs for no > obvious reason (to me). > > > > >> Maybe this deserves it's own thread... > > > > Done :-) > > > >>>> I am all in for this change also, personally I would have changed to > >>>> printf for now > >>>> to have one less dependency (also an easy port to C++20 std::format). The > >>>> dependency list is getting large, I noticed a build of mine failing > >>>> recently because > >>>> I didn't have libyaml. > >>> > >>> I've posted a patch that falls back to a subproject in that case. Would > >>> you be able to give it a try ? > >>> > >>>> But std::format and libfmt are quite fast and anything is better than > >>>> streams so +1. > >>>> > >>>>> I've never used (yet) {fmt}, but I've only heard good things about > >>>>> performance, and of course it's headed into the standard, so I also > >>>>> think there is some good merit to be found in this development. > > > Only speaking for the red hat distros, every distro that has a package built for libcamera or could have in future has at least C++17, including newly released LTS release RHEL9. Given libcamera is still 0.0 it could be a nice opportunity to take.
On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote: > On Sat, 28 May 2022 at 00:19, Christian Rauch wrote: > > Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: > > > On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: > > >> Quoting Laurent Pinchart (2022-05-10 14:04:57) > > >>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > > >>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > > >>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) > > >>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > > > > > > [snip] > > > > > >>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and > > >>>>>> libfmt is a wonderful alternative. > > >>>>>> It also is close enough to the C++20 std::format implementation that > > >>>>>> eventual porting would be low effort. So I am all for this change :) > > >>> > > >>> It's going to take a while before we can switch to C++20, but I'm > > >>> looking forward to it (and before that to some of the C++17 features > > >>> too, it would be nice to use std::optional in the public API). > > >> > > >> So, I've just seen as Christian said - we already do! > > >> > > >> It's used by color space .. > > > > > > Oops... > > > > > >> So we're faced with either reverting, or rolling back on the color-space > > >> change, or pushing forwards with C++17 on the public-api. > > > > > > Switching to C++17 worries me as it's fairly recent and may not be > > > easily available for all users. std::optional could easily rbe replaced > > > with a custom implementation in utils::optional if needed. I'm tempted > > > to leave std::optional in for now, and accept new users of std::optional > > > but no other C++17 APIs. Switching to utils::optional for ColorSpace > > > would cover new users of std::optional so we're not creating any new > > > liability. > > > > I think that C++17 is quite old by now (nearly 5 years) and all major > > compilers support it for multiple versions and years now. Do you know of > > an example, where requiring C++17 would hinder the adaptation of > > libcamera? Do any of the target system not support this standard? The main blocker used to tbe Chromium, which used C++14. It seems to have moved to C++17 now, and we have also dropped the plan for direct integration of libcamera support in Chromium (and Firefox) as they should go through PipeWire and the XDG camera portal. > > I think the use of std::optional to express invalid values is quite > > significant compared to the previous solution of using default > > constructed values. Reimplementing this in libcamera would be a > > solution, but it just adds development and maintenance costs for no > > obvious reason (to me). We can use std::optional (as it's there already). *If* C++17 support ends up being an issue in the future, requiring us to move back to C++14, then it will be easy to make a custom implementation, but only if necessary (we've done that with std::span already, which is only available in C++20). > > >> Maybe this deserves it's own thread... > > > > > > Done :-) > > > > > >>>> I am all in for this change also, personally I would have changed to > > >>>> printf for now > > >>>> to have one less dependency (also an easy port to C++20 std::format). The > > >>>> dependency list is getting large, I noticed a build of mine failing > > >>>> recently because > > >>>> I didn't have libyaml. > > >>> > > >>> I've posted a patch that falls back to a subproject in that case. Would > > >>> you be able to give it a try ? > > >>> > > >>>> But std::format and libfmt are quite fast and anything is better than > > >>>> streams so +1. > > >>>> > > >>>>> I've never used (yet) {fmt}, but I've only heard good things about > > >>>>> performance, and of course it's headed into the standard, so I also > > >>>>> think there is some good merit to be found in this development. > > Only speaking for the red hat distros, every distro that has a package > built for libcamera or could have in future has at least C++17, > including newly released LTS release RHEL9. That's good to know. The other question is whether all C++ applications that may want to use libcamera can be compiled with C++17 or newer. > Given libcamera is still 0.0 it could be a nice opportunity to take.
Am 30.05.22 um 10:03 schrieb Laurent Pinchart: > On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote: >> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote: >>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: >>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: >>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57) >>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: >>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: >>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) >>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: >>>> >>>> [snip] >>>> >>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and >>>>>>>>> libfmt is a wonderful alternative. >>>>>>>>> It also is close enough to the C++20 std::format implementation that >>>>>>>>> eventual porting would be low effort. So I am all for this change :) >>>>>> >>>>>> It's going to take a while before we can switch to C++20, but I'm >>>>>> looking forward to it (and before that to some of the C++17 features >>>>>> too, it would be nice to use std::optional in the public API). >>>>> >>>>> So, I've just seen as Christian said - we already do! >>>>> >>>>> It's used by color space .. >>>> >>>> Oops... >>>> >>>>> So we're faced with either reverting, or rolling back on the color-space >>>>> change, or pushing forwards with C++17 on the public-api. >>>> >>>> Switching to C++17 worries me as it's fairly recent and may not be >>>> easily available for all users. std::optional could easily rbe replaced >>>> with a custom implementation in utils::optional if needed. I'm tempted >>>> to leave std::optional in for now, and accept new users of std::optional >>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace >>>> would cover new users of std::optional so we're not creating any new >>>> liability. >>> >>> I think that C++17 is quite old by now (nearly 5 years) and all major >>> compilers support it for multiple versions and years now. Do you know of >>> an example, where requiring C++17 would hinder the adaptation of >>> libcamera? Do any of the target system not support this standard? > > The main blocker used to tbe Chromium, which used C++14. It seems to > have moved to C++17 now, and we have also dropped the plan for direct > integration of libcamera support in Chromium (and Firefox) as they > should go through PipeWire and the XDG camera portal. > >>> I think the use of std::optional to express invalid values is quite >>> significant compared to the previous solution of using default >>> constructed values. Reimplementing this in libcamera would be a >>> solution, but it just adds development and maintenance costs for no >>> obvious reason (to me). > > We can use std::optional (as it's there already). *If* C++17 support > ends up being an issue in the future, requiring us to move back to > C++14, then it will be easy to make a custom implementation, but only if > necessary (we've done that with std::span already, which is only > available in C++20). > >>>>> Maybe this deserves it's own thread... >>>> >>>> Done :-) >>>> >>>>>>> I am all in for this change also, personally I would have changed to >>>>>>> printf for now >>>>>>> to have one less dependency (also an easy port to C++20 std::format). The >>>>>>> dependency list is getting large, I noticed a build of mine failing >>>>>>> recently because >>>>>>> I didn't have libyaml. >>>>>> >>>>>> I've posted a patch that falls back to a subproject in that case. Would >>>>>> you be able to give it a try ? >>>>>> >>>>>>> But std::format and libfmt are quite fast and anything is better than >>>>>>> streams so +1. >>>>>>> >>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about >>>>>>>> performance, and of course it's headed into the standard, so I also >>>>>>>> think there is some good merit to be found in this development. >> >> Only speaking for the red hat distros, every distro that has a package >> built for libcamera or could have in future has at least C++17, >> including newly released LTS release RHEL9. > > That's good to know. The other question is whether all C++ applications > that may want to use libcamera can be compiled with C++17 or newer. Isn't C++17 backwards compatible? My understanding is that every earlier C++ standard (e.g. C++11) should be able to compile with a C++17 (or later) compiler. > >> Given libcamera is still 0.0 it could be a nice opportunity to take. >
Hi Christian, On Mon, May 30, 2022 at 11:11:23AM +0100, Christian Rauch wrote: > Am 30.05.22 um 10:03 schrieb Laurent Pinchart: > > On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote: > >> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote: > >>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: > >>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: > >>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57) > >>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > >>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > >>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) > >>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > >>>> > >>>> [snip] > >>>> > >>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and > >>>>>>>>> libfmt is a wonderful alternative. > >>>>>>>>> It also is close enough to the C++20 std::format implementation that > >>>>>>>>> eventual porting would be low effort. So I am all for this change :) > >>>>>> > >>>>>> It's going to take a while before we can switch to C++20, but I'm > >>>>>> looking forward to it (and before that to some of the C++17 features > >>>>>> too, it would be nice to use std::optional in the public API). > >>>>> > >>>>> So, I've just seen as Christian said - we already do! > >>>>> > >>>>> It's used by color space .. > >>>> > >>>> Oops... > >>>> > >>>>> So we're faced with either reverting, or rolling back on the color-space > >>>>> change, or pushing forwards with C++17 on the public-api. > >>>> > >>>> Switching to C++17 worries me as it's fairly recent and may not be > >>>> easily available for all users. std::optional could easily rbe replaced > >>>> with a custom implementation in utils::optional if needed. I'm tempted > >>>> to leave std::optional in for now, and accept new users of std::optional > >>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace > >>>> would cover new users of std::optional so we're not creating any new > >>>> liability. > >>> > >>> I think that C++17 is quite old by now (nearly 5 years) and all major > >>> compilers support it for multiple versions and years now. Do you know of > >>> an example, where requiring C++17 would hinder the adaptation of > >>> libcamera? Do any of the target system not support this standard? > > > > The main blocker used to tbe Chromium, which used C++14. It seems to > > have moved to C++17 now, and we have also dropped the plan for direct > > integration of libcamera support in Chromium (and Firefox) as they > > should go through PipeWire and the XDG camera portal. > > > >>> I think the use of std::optional to express invalid values is quite > >>> significant compared to the previous solution of using default > >>> constructed values. Reimplementing this in libcamera would be a > >>> solution, but it just adds development and maintenance costs for no > >>> obvious reason (to me). > > > > We can use std::optional (as it's there already). *If* C++17 support > > ends up being an issue in the future, requiring us to move back to > > C++14, then it will be easy to make a custom implementation, but only if > > necessary (we've done that with std::span already, which is only > > available in C++20). > > > >>>>> Maybe this deserves it's own thread... > >>>> > >>>> Done :-) > >>>> > >>>>>>> I am all in for this change also, personally I would have changed to > >>>>>>> printf for now > >>>>>>> to have one less dependency (also an easy port to C++20 std::format). The > >>>>>>> dependency list is getting large, I noticed a build of mine failing > >>>>>>> recently because > >>>>>>> I didn't have libyaml. > >>>>>> > >>>>>> I've posted a patch that falls back to a subproject in that case. Would > >>>>>> you be able to give it a try ? > >>>>>> > >>>>>>> But std::format and libfmt are quite fast and anything is better than > >>>>>>> streams so +1. > >>>>>>> > >>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about > >>>>>>>> performance, and of course it's headed into the standard, so I also > >>>>>>>> think there is some good merit to be found in this development. > >> > >> Only speaking for the red hat distros, every distro that has a package > >> built for libcamera or could have in future has at least C++17, > >> including newly released LTS release RHEL9. > > > > That's good to know. The other question is whether all C++ applications > > that may want to use libcamera can be compiled with C++17 or newer. > > Isn't C++17 backwards compatible? My understanding is that every earlier > C++ standard (e.g. C++11) should be able to compile with a C++17 (or > later) compiler. Not quite I'm afraid. -------- #include <map> /* This includes <optional> indirectly in libc++ */ using namespace std; template<class T> class optional { public: optional() { } }; int main() { optional<int> opt; return 0; } -------- $ clang++ -W -Wall -stdlib=libc++ -std=c++14 -o version version.cpp $ clang++ -W -Wall -stdlib=libc++ -std=c++17 -o version version.cpp version.cpp:16:2: error: reference to 'optional' is ambiguous optional<int> opt; ^ version.cpp:6:7: note: candidate found by name lookup is 'optional' class optional ^ /usr/include/c++/v1/optional:590:7: note: candidate found by name lookup is 'std::optional' class optional ^ 1 error generated. > >> Given libcamera is still 0.0 it could be a nice opportunity to take.
Hi Laurent, Am 30.05.22 um 11:37 schrieb Laurent Pinchart: > Hi Christian, > > On Mon, May 30, 2022 at 11:11:23AM +0100, Christian Rauch wrote: >> Am 30.05.22 um 10:03 schrieb Laurent Pinchart: >>> On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote: >>>> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote: >>>>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: >>>>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: >>>>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57) >>>>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: >>>>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: >>>>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) >>>>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: >>>>>> >>>>>> [snip] >>>>>> >>>>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and >>>>>>>>>>> libfmt is a wonderful alternative. >>>>>>>>>>> It also is close enough to the C++20 std::format implementation that >>>>>>>>>>> eventual porting would be low effort. So I am all for this change :) >>>>>>>> >>>>>>>> It's going to take a while before we can switch to C++20, but I'm >>>>>>>> looking forward to it (and before that to some of the C++17 features >>>>>>>> too, it would be nice to use std::optional in the public API). >>>>>>> >>>>>>> So, I've just seen as Christian said - we already do! >>>>>>> >>>>>>> It's used by color space .. >>>>>> >>>>>> Oops... >>>>>> >>>>>>> So we're faced with either reverting, or rolling back on the color-space >>>>>>> change, or pushing forwards with C++17 on the public-api. >>>>>> >>>>>> Switching to C++17 worries me as it's fairly recent and may not be >>>>>> easily available for all users. std::optional could easily rbe replaced >>>>>> with a custom implementation in utils::optional if needed. I'm tempted >>>>>> to leave std::optional in for now, and accept new users of std::optional >>>>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace >>>>>> would cover new users of std::optional so we're not creating any new >>>>>> liability. >>>>> >>>>> I think that C++17 is quite old by now (nearly 5 years) and all major >>>>> compilers support it for multiple versions and years now. Do you know of >>>>> an example, where requiring C++17 would hinder the adaptation of >>>>> libcamera? Do any of the target system not support this standard? >>> >>> The main blocker used to tbe Chromium, which used C++14. It seems to >>> have moved to C++17 now, and we have also dropped the plan for direct >>> integration of libcamera support in Chromium (and Firefox) as they >>> should go through PipeWire and the XDG camera portal. >>> >>>>> I think the use of std::optional to express invalid values is quite >>>>> significant compared to the previous solution of using default >>>>> constructed values. Reimplementing this in libcamera would be a >>>>> solution, but it just adds development and maintenance costs for no >>>>> obvious reason (to me). >>> >>> We can use std::optional (as it's there already). *If* C++17 support >>> ends up being an issue in the future, requiring us to move back to >>> C++14, then it will be easy to make a custom implementation, but only if >>> necessary (we've done that with std::span already, which is only >>> available in C++20). >>> >>>>>>> Maybe this deserves it's own thread... >>>>>> >>>>>> Done :-) >>>>>> >>>>>>>>> I am all in for this change also, personally I would have changed to >>>>>>>>> printf for now >>>>>>>>> to have one less dependency (also an easy port to C++20 std::format). The >>>>>>>>> dependency list is getting large, I noticed a build of mine failing >>>>>>>>> recently because >>>>>>>>> I didn't have libyaml. >>>>>>>> >>>>>>>> I've posted a patch that falls back to a subproject in that case. Would >>>>>>>> you be able to give it a try ? >>>>>>>> >>>>>>>>> But std::format and libfmt are quite fast and anything is better than >>>>>>>>> streams so +1. >>>>>>>>> >>>>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about >>>>>>>>>> performance, and of course it's headed into the standard, so I also >>>>>>>>>> think there is some good merit to be found in this development. >>>> >>>> Only speaking for the red hat distros, every distro that has a package >>>> built for libcamera or could have in future has at least C++17, >>>> including newly released LTS release RHEL9. >>> >>> That's good to know. The other question is whether all C++ applications >>> that may want to use libcamera can be compiled with C++17 or newer. >> >> Isn't C++17 backwards compatible? My understanding is that every earlier >> C++ standard (e.g. C++11) should be able to compile with a C++17 (or >> later) compiler. > > Not quite I'm afraid. > > -------- > #include <map> /* This includes <optional> indirectly in libc++ */ > > using namespace std; > > template<class T> > class optional > { > public: > optional() > { > } > }; > > int main() > { > optional<int> opt; > > return 0; > } Right, this is clashing because of ambiguous symbols from different namespaces. You have a point there. I only thought in terms of API compatibility there. Anyway, libcamera already requires C++17 and uses std::optional. I would like to push for a decision if libcamera: A) continues to use C++17 and std::optional B) reverts those changes and implements a custom "optional" I am in favour of keeping the status quo of using std::optional. The reason is that I think that not many projects (if any) will be affected by these kinds of ambiguity issues above. Resorting to a custom solution, just to switch to std::optional in the public API later (when?), will cause more breakage issues. A custom temporary solution will also require someone to implement and maintain this, knowing that it will be replaced eventually anyway. I simply think that the benefits outweigh the potential issues. Using std::optional, for example as control value return type, improves the descriptiveness of the API. While on the other side, there seem to be no projects struggling with the current C++17/std::optional situation. Sorry for pushing for a solution, but I really would like to continue with my original patch set :-) Best, Christian > -------- > > $ clang++ -W -Wall -stdlib=libc++ -std=c++14 -o version version.cpp > $ clang++ -W -Wall -stdlib=libc++ -std=c++17 -o version version.cpp > version.cpp:16:2: error: reference to 'optional' is ambiguous > optional<int> opt; > ^ > version.cpp:6:7: note: candidate found by name lookup is 'optional' > class optional > ^ > /usr/include/c++/v1/optional:590:7: note: candidate found by name lookup is 'std::optional' > class optional > ^ > 1 error generated. > >>>> Given libcamera is still 0.0 it could be a nice opportunity to take. >
Hi Christian, On Mon, May 30, 2022 at 11:20:06PM +0100, Christian Rauch via libcamera-devel wrote: > Am 30.05.22 um 11:37 schrieb Laurent Pinchart: > > On Mon, May 30, 2022 at 11:11:23AM +0100, Christian Rauch wrote: > >> Am 30.05.22 um 10:03 schrieb Laurent Pinchart: > >>> On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote: > >>>> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote: > >>>>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel: > >>>>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote: > >>>>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57) > >>>>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote: > >>>>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote: > >>>>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14) > >>>>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote: > >>>>>> > >>>>>> [snip] > >>>>>> > >>>>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and > >>>>>>>>>>> libfmt is a wonderful alternative. > >>>>>>>>>>> It also is close enough to the C++20 std::format implementation that > >>>>>>>>>>> eventual porting would be low effort. So I am all for this change :) > >>>>>>>> > >>>>>>>> It's going to take a while before we can switch to C++20, but I'm > >>>>>>>> looking forward to it (and before that to some of the C++17 features > >>>>>>>> too, it would be nice to use std::optional in the public API). > >>>>>>> > >>>>>>> So, I've just seen as Christian said - we already do! > >>>>>>> > >>>>>>> It's used by color space .. > >>>>>> > >>>>>> Oops... > >>>>>> > >>>>>>> So we're faced with either reverting, or rolling back on the color-space > >>>>>>> change, or pushing forwards with C++17 on the public-api. > >>>>>> > >>>>>> Switching to C++17 worries me as it's fairly recent and may not be > >>>>>> easily available for all users. std::optional could easily rbe replaced > >>>>>> with a custom implementation in utils::optional if needed. I'm tempted > >>>>>> to leave std::optional in for now, and accept new users of std::optional > >>>>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace > >>>>>> would cover new users of std::optional so we're not creating any new > >>>>>> liability. > >>>>> > >>>>> I think that C++17 is quite old by now (nearly 5 years) and all major > >>>>> compilers support it for multiple versions and years now. Do you know of > >>>>> an example, where requiring C++17 would hinder the adaptation of > >>>>> libcamera? Do any of the target system not support this standard? > >>> > >>> The main blocker used to tbe Chromium, which used C++14. It seems to > >>> have moved to C++17 now, and we have also dropped the plan for direct > >>> integration of libcamera support in Chromium (and Firefox) as they > >>> should go through PipeWire and the XDG camera portal. > >>> > >>>>> I think the use of std::optional to express invalid values is quite > >>>>> significant compared to the previous solution of using default > >>>>> constructed values. Reimplementing this in libcamera would be a > >>>>> solution, but it just adds development and maintenance costs for no > >>>>> obvious reason (to me). > >>> > >>> We can use std::optional (as it's there already). *If* C++17 support > >>> ends up being an issue in the future, requiring us to move back to > >>> C++14, then it will be easy to make a custom implementation, but only if > >>> necessary (we've done that with std::span already, which is only > >>> available in C++20). > >>> > >>>>>>> Maybe this deserves it's own thread... > >>>>>> > >>>>>> Done :-) > >>>>>> > >>>>>>>>> I am all in for this change also, personally I would have changed to > >>>>>>>>> printf for now > >>>>>>>>> to have one less dependency (also an easy port to C++20 std::format). The > >>>>>>>>> dependency list is getting large, I noticed a build of mine failing > >>>>>>>>> recently because > >>>>>>>>> I didn't have libyaml. > >>>>>>>> > >>>>>>>> I've posted a patch that falls back to a subproject in that case. Would > >>>>>>>> you be able to give it a try ? > >>>>>>>> > >>>>>>>>> But std::format and libfmt are quite fast and anything is better than > >>>>>>>>> streams so +1. > >>>>>>>>> > >>>>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about > >>>>>>>>>> performance, and of course it's headed into the standard, so I also > >>>>>>>>>> think there is some good merit to be found in this development. > >>>> > >>>> Only speaking for the red hat distros, every distro that has a package > >>>> built for libcamera or could have in future has at least C++17, > >>>> including newly released LTS release RHEL9. > >>> > >>> That's good to know. The other question is whether all C++ applications > >>> that may want to use libcamera can be compiled with C++17 or newer. > >> > >> Isn't C++17 backwards compatible? My understanding is that every earlier > >> C++ standard (e.g. C++11) should be able to compile with a C++17 (or > >> later) compiler. > > > > Not quite I'm afraid. > > > > -------- > > #include <map> /* This includes <optional> indirectly in libc++ */ > > > > using namespace std; > > > > template<class T> > > class optional > > { > > public: > > optional() > > { > > } > > }; > > > > int main() > > { > > optional<int> opt; > > > > return 0; > > } > > Right, this is clashing because of ambiguous symbols from different > namespaces. You have a point there. I only thought in terms of API > compatibility there. > > Anyway, libcamera already requires C++17 and uses std::optional. I would > like to push for a decision if libcamera: > A) continues to use C++17 and std::optional > B) reverts those changes and implements a custom "optional" > > I am in favour of keeping the status quo of using std::optional. The > reason is that I think that not many projects (if any) will be affected > by these kinds of ambiguity issues above. Resorting to a custom > solution, just to switch to std::optional in the public API later > (when?), will cause more breakage issues. A custom temporary solution > will also require someone to implement and maintain this, knowing that > it will be replaced eventually anyway. > > I simply think that the benefits outweigh the potential issues. Using > std::optional, for example as control value return type, improves the > descriptiveness of the API. While on the other side, there seem to be no > projects struggling with the current C++17/std::optional situation. > > Sorry for pushing for a solution, but I really would like to continue > with my original patch set :-) We may have trouble understanding each other. I'm fine with std::optional being used in the public API for the time being. *If and only if* we later realize C++17 is causing issues in some important use cases, then we can switch to a custom utils::optional implementation. The risk of having to do so seems low to me. Tl;dr: More usage of std::optional in the public API is fine. > > -------- > > > > $ clang++ -W -Wall -stdlib=libc++ -std=c++14 -o version version.cpp > > $ clang++ -W -Wall -stdlib=libc++ -std=c++17 -o version version.cpp > > version.cpp:16:2: error: reference to 'optional' is ambiguous > > optional<int> opt; > > ^ > > version.cpp:6:7: note: candidate found by name lookup is 'optional' > > class optional > > ^ > > /usr/include/c++/v1/optional:590:7: note: candidate found by name lookup is 'std::optional' > > class optional > > ^ > > 1 error generated. > > > >>>> Given libcamera is still 0.0 it could be a nice opportunity to take.
diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp index efffafbf..7843c3fd 100644 --- a/src/cam/camera_session.cpp +++ b/src/cam/camera_session.cpp @@ -5,10 +5,9 @@ * camera_session.cpp - Camera capture session */ -#include <iomanip> -#include <iostream> #include <limits.h> -#include <sstream> +#include <fmt/format.h> +#include <fmt/ostream.h> #include <libcamera/control_ids.h> #include <libcamera/property_ids.h> @@ -22,6 +21,9 @@ #include "main.h" #include "stream_options.h" +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) + using namespace libcamera; CameraSession::CameraSession(CameraManager *cm, @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm, camera_ = cm->get(cameraId); if (!camera_) { - std::cerr << "Camera " << cameraId << " not found" << std::endl; + EPR("Camera {} not found\n", cameraId); return; } if (camera_->acquire()) { - std::cerr << "Failed to acquire camera " << cameraId - << std::endl; + EPR("Failed to acquire camera {}", cameraId); return; } @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm, std::unique_ptr<CameraConfiguration> config = camera_->generateConfiguration(roles); if (!config || config->size() != roles.size()) { - std::cerr << "Failed to get default stream configuration" - << std::endl; + EPR("Failed to get default stream configuration\n"); return; } /* Apply configuration if explicitly requested. */ if (StreamKeyValueParser::updateConfiguration(config.get(), options_[OptStream])) { - std::cerr << "Failed to update configuration" << std::endl; + EPR("Failed to update configuration\n"); return; } @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm, #ifdef HAVE_KMS if (options_.isSet(OptDisplay)) { if (options_.isSet(OptFile)) { - std::cerr << "--display and --file options are mutually exclusive" - << std::endl; + EPR("--display and --file options are mutually exclusive\n"); return; } if (roles.size() != 1) { - std::cerr << "Display doesn't support multiple streams" - << std::endl; + EPR("Display doesn't support multiple streams\n"); return; } if (roles[0] != StreamRole::Viewfinder) { - std::cerr << "Display requires a viewfinder stream" - << std::endl; + EPR("Display requires a viewfinder stream\n"); return; } } @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm, case CameraConfiguration::Adjusted: if (strictFormats) { - std::cout << "Adjusting camera configuration disallowed by --strict-formats argument" - << std::endl; + PR("Adjusting camera configuration disallowed by --strict-formats argument\n"); return; } - std::cout << "Camera configuration adjusted" << std::endl; + PR("Camera configuration adjusted\n"); break; case CameraConfiguration::Invalid: - std::cout << "Camera configuration invalid" << std::endl; + PR("Camera configuration invalid\n"); return; } @@ -121,8 +117,7 @@ CameraSession::~CameraSession() void CameraSession::listControls() const { for (const auto &[id, info] : camera_->controls()) { - std::cout << "Control: " << id->name() << ": " - << info.toString() << std::endl; + PR("Control: {}: {}}n", id->name(), info.toString()); } } @@ -131,8 +126,7 @@ void CameraSession::listProperties() const for (const auto &[key, value] : camera_->properties()) { const ControlId *id = properties::properties.at(key); - std::cout << "Property: " << id->name() << " = " - << value.toString() << std::endl; + PR("Property: {} = {}\n", id->name(), value.toString()); } } @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const { unsigned int index = 0; for (const StreamConfiguration &cfg : *config_) { - std::cout << index << ": " << cfg.toString() << std::endl; + PR("{}: {}\n", index, cfg.toString()); const StreamFormats &formats = cfg.formats(); for (PixelFormat pixelformat : formats.pixelformats()) { - std::cout << " * Pixelformat: " - << pixelformat << " " - << formats.range(pixelformat).toString() - << std::endl; + PR(" * Pixelformat: {} {}\n", pixelformat, + formats.range(pixelformat).toString()); for (const Size &size : formats.sizes(pixelformat)) - std::cout << " - " << size << std::endl; + PR(" - {}\n", size); } index++; @@ -168,7 +160,7 @@ int CameraSession::start() ret = camera_->configure(config_.get()); if (ret < 0) { - std::cout << "Failed to configure camera" << std::endl; + PR("Failed to configure camera\n"); return ret; } @@ -197,8 +189,7 @@ int CameraSession::start() if (sink_) { ret = sink_->configure(*config_); if (ret < 0) { - std::cout << "Failed to configure frame sink" - << std::endl; + PR("Failed to configure frame sink\n"); return ret; } @@ -214,12 +205,12 @@ void CameraSession::stop() { int ret = camera_->stop(); if (ret) - std::cout << "Failed to stop capture" << std::endl; + PR("Failed to stop capture\n"); if (sink_) { ret = sink_->stop(); if (ret) - std::cout << "Failed to stop frame sink" << std::endl; + PR("Failed to stop frame sink\n"); } sink_.reset(); @@ -238,7 +229,7 @@ int CameraSession::startCapture() for (StreamConfiguration &cfg : *config_) { ret = allocator_->allocate(cfg.stream()); if (ret < 0) { - std::cerr << "Can't allocate buffers" << std::endl; + EPR("Can't allocate buffers\n"); return -ENOMEM; } @@ -254,7 +245,7 @@ int CameraSession::startCapture() for (unsigned int i = 0; i < nbuffers; i++) { std::unique_ptr<Request> request = camera_->createRequest(); if (!request) { - std::cerr << "Can't create request" << std::endl; + EPR("Can't create request\n"); return -ENOMEM; } @@ -266,8 +257,7 @@ int CameraSession::startCapture() ret = request->addBuffer(stream, buffer.get()); if (ret < 0) { - std::cerr << "Can't set buffer for request" - << std::endl; + EPR("Can't set buffer for request\n"); return ret; } @@ -281,14 +271,14 @@ int CameraSession::startCapture() if (sink_) { ret = sink_->start(); if (ret) { - std::cout << "Failed to start frame sink" << std::endl; + PR("Failed to start frame sink\n"); return ret; } } ret = camera_->start(); if (ret) { - std::cout << "Failed to start capture" << std::endl; + PR("Failed to start capture\n"); if (sink_) sink_->stop(); return ret; @@ -297,7 +287,7 @@ int CameraSession::startCapture() for (std::unique_ptr<Request> &request : requests_) { ret = queueRequest(request.get()); if (ret < 0) { - std::cerr << "Can't queue request" << std::endl; + EPR("Can't queue request\n"); camera_->stop(); if (sink_) sink_->stop(); @@ -306,13 +296,11 @@ int CameraSession::startCapture() } if (captureLimit_) - std::cout << "cam" << cameraIndex_ - << ": Capture " << captureLimit_ << " frames" - << std::endl; + PR("cam{}: Capture {} frames\n", cameraIndex_, + captureLimit_); else - std::cout << "cam" << cameraIndex_ - << ": Capture until user interrupts by SIGINT" - << std::endl; + PR("cam{}: Capture until user interrupts by SIGINT\n", + cameraIndex_); return 0; } @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request) bool requeue = true; - std::stringstream info; - info << ts / 1000000000 << "." - << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000 - << " (" << std::fixed << std::setprecision(2) << fps << " fps)"; + auto sbuf = fmt::memory_buffer(); + fmt::format_to(std::back_inserter(sbuf), "{}.{:06} ({:.2f} fps)", + ts / 1000000000, + ts / 1000 % 1000000, + fps); for (const auto &[stream, buffer] : buffers) { const FrameMetadata &metadata = buffer->metadata(); - info << " " << streamNames_[stream] - << " seq: " << std::setw(6) << std::setfill('0') << metadata.sequence - << " bytesused: "; + fmt::format_to(std::back_inserter(sbuf), " {} seq: {:06} bytesused: ", + streamNames_[stream], metadata.sequence); unsigned int nplane = 0; for (const FrameMetadata::Plane &plane : metadata.planes()) { - info << plane.bytesused; + fmt::format_to(std::back_inserter(sbuf), "{}", plane.bytesused); if (++nplane < metadata.planes().size()) - info << "/"; + fmt::format_to(std::back_inserter(sbuf), "/"); } } @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request) requeue = false; } - std::cout << info.str() << std::endl; + PR("{}\n", fmt::to_string(sbuf)); if (printMetadata_) { const ControlList &requestMetadata = request->metadata(); for (const auto &[key, value] : requestMetadata) { const ControlId *id = controls::controls.at(key); - std::cout << "\t" << id->name() << " = " - << value.toString() << std::endl; + PR("\t{} = {}\n", id->name(), value.toString()); } } diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp index 46e34eb5..84919ab3 100644 --- a/src/cam/drm.cpp +++ b/src/cam/drm.cpp @@ -10,12 +10,12 @@ #include <algorithm> #include <errno.h> #include <fcntl.h> -#include <iostream> #include <set> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> +#include <fmt/core.h> #include <libcamera/framebuffer.h> #include <libcamera/geometry.h> @@ -25,6 +25,9 @@ #include "event_loop.h" +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) + namespace DRM { Object::Object(Device *dev, uint32_t id, Type type) @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const drmModeConnector *connector) { auto typeName = connectorTypeNames.find(connector->connector_type); if (typeName == connectorTypeNames.end()) { - std::cerr - << "Invalid connector type " - << connector->connector_type << std::endl; + EPR("Invalid connector type {}}n", connector->connector_type); typeName = connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown); } @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const drmModeConnector *connector) return e.id() == encoderId; }); if (encoder == encoders.end()) { - std::cerr - << "Encoder " << encoderId << " not found" - << std::endl; + EPR("Encoder {} not found\n", encoderId); continue; } @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer() if (ret == -1) { ret = -errno; - std::cerr - << "Failed to close GEM object: " - << strerror(-ret) << std::endl; + EPR("Failed to close GEM object: {}\n", strerror(-ret)); } } @@ -408,9 +405,8 @@ int Device::init() fd_ = open(name, O_RDWR | O_CLOEXEC); if (fd_ < 0) { ret = -errno; - std::cerr - << "Failed to open DRM/KMS device " << name << ": " - << strerror(-ret) << std::endl; + EPR("Failed to open DRM/KMS device {}: {}\n", name, + strerror(-ret)); return ret; } @@ -421,9 +417,7 @@ int Device::init() ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1); if (ret < 0) { ret = -errno; - std::cerr - << "Failed to enable atomic capability: " - << strerror(-ret) << std::endl; + EPR("Failed to enable atomic capability: {}\n", strerror(-ret)); return ret; } @@ -448,9 +442,7 @@ int Device::getResources() }; if (!resources) { ret = -errno; - std::cerr - << "Failed to get DRM/KMS resources: " - << strerror(-ret) << std::endl; + EPR("Failed to get DRM/KMS resources: {}\n", strerror(-ret)); return ret; } @@ -458,9 +450,7 @@ int Device::getResources() drmModeCrtc *crtc = drmModeGetCrtc(fd_, resources->crtcs[i]); if (!crtc) { ret = -errno; - std::cerr - << "Failed to get CRTC: " << strerror(-ret) - << std::endl; + EPR("Failed to get CRTC: {}\n", strerror(-ret)); return ret; } @@ -476,9 +466,7 @@ int Device::getResources() drmModeGetEncoder(fd_, resources->encoders[i]); if (!encoder) { ret = -errno; - std::cerr - << "Failed to get encoder: " << strerror(-ret) - << std::endl; + EPR("Failed to get encoder: {}\n", strerror(-ret)); return ret; } @@ -494,9 +482,7 @@ int Device::getResources() drmModeGetConnector(fd_, resources->connectors[i]); if (!connector) { ret = -errno; - std::cerr - << "Failed to get connector: " << strerror(-ret) - << std::endl; + EPR("Failed to get connector: {}\n", strerror(-ret)); return ret; } @@ -513,9 +499,7 @@ int Device::getResources() }; if (!planes) { ret = -errno; - std::cerr - << "Failed to get DRM/KMS planes: " - << strerror(-ret) << std::endl; + EPR("Failed to get DRM/KMS planes: {}\n", strerror(-ret)); return ret; } @@ -524,9 +508,7 @@ int Device::getResources() drmModeGetPlane(fd_, planes->planes[i]); if (!plane) { ret = -errno; - std::cerr - << "Failed to get plane: " << strerror(-ret) - << std::endl; + EPR("Failed to get plane: {}\n", strerror(-ret)); return ret; } @@ -556,9 +538,7 @@ int Device::getResources() drmModePropertyRes *property = drmModeGetProperty(fd_, id); if (!property) { ret = -errno; - std::cerr - << "Failed to get property: " << strerror(-ret) - << std::endl; + EPR("Failed to get property: {}\n", strerror(-ret)); continue; } @@ -573,9 +553,8 @@ int Device::getResources() for (auto &object : objects_) { ret = object.second->setup(); if (ret < 0) { - std::cerr - << "Failed to setup object " << object.second->id() - << ": " << strerror(-ret) << std::endl; + EPR("Failed to setup object {}: {}\n", + object.second->id(), strerror(-ret)); return ret; } } @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( ret = drmPrimeFDToHandle(fd_, plane.fd.get(), &handle); if (ret < 0) { ret = -errno; - std::cerr - << "Unable to import framebuffer dmabuf: " - << strerror(-ret) << std::endl; + EPR("Unable to import framebuffer dmabuf: {}\n", + strerror(-ret)); return nullptr; } @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer( strides.data(), offsets, &fb->id_, 0); if (ret < 0) { ret = -errno; - std::cerr - << "Failed to add framebuffer: " - << strerror(-ret) << std::endl; + EPR("Failed to add framebuffer: {}\n", strerror(-ret)); return nullptr; } diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp index e25784c0..87aaf59a 100644 --- a/src/cam/event_loop.cpp +++ b/src/cam/event_loop.cpp @@ -10,7 +10,10 @@ #include <assert.h> #include <event2/event.h> #include <event2/thread.h> -#include <iostream> +#include <fmt/core.h> + +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) EventLoop *EventLoop::instance_ = nullptr; @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type, event->event_ = event_new(base_, fd, events, &EventLoop::Event::dispatch, event.get()); if (!event->event_) { - std::cerr << "Failed to create event for fd " << fd << std::endl; + EPR("Failed to create event for fd {}\n", fd); return; } int ret = event_add(event->event_, nullptr); if (ret < 0) { - std::cerr << "Failed to add event for fd " << fd << std::endl; + EPR("Failed to add event for fd {}\n", fd); return; } diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp index 45213d4a..86e2118c 100644 --- a/src/cam/file_sink.cpp +++ b/src/cam/file_sink.cpp @@ -7,11 +7,12 @@ #include <assert.h> #include <fcntl.h> -#include <iomanip> -#include <iostream> -#include <sstream> #include <string.h> #include <unistd.h> +#include <fmt/core.h> + +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) #include <libcamera/camera.h> @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer) pos = filename.find_first_of('#'); if (pos != std::string::npos) { - std::stringstream ss; - ss << streamNames_[stream] << "-" << std::setw(6) - << std::setfill('0') << buffer->metadata().sequence; - filename.replace(pos, 1, ss.str()); + auto s = fmt::format("{}-{:06}", + streamNames_[stream], + buffer->metadata().sequence); + filename.replace(pos, 1, s); } fd = open(filename.c_str(), O_CREAT | O_WRONLY | @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer) S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd == -1) { ret = -errno; - std::cerr << "failed to open file " << filename << ": " - << strerror(-ret) << std::endl; + EPR("failed to open file {}: {}\n", filename, strerror(-ret)); return; } @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream, FrameBuffer *buffer) unsigned int length = std::min<unsigned int>(meta.bytesused, data.size()); if (meta.bytesused > data.size()) - std::cerr << "payload size " << meta.bytesused - << " larger than plane size " << data.size() - << std::endl; + EPR("payload size {} larger than plane size {}\n", + meta.bytesused, data.size()); ret = ::write(fd, data.data(), length); if (ret < 0) { ret = -errno; - std::cerr << "write error: " << strerror(-ret) - << std::endl; + EPR("write error: {}\n", strerror(-ret)); break; } else if (ret != (int)length) { - std::cerr << "write error: only " << ret - << " bytes written instead of " - << length << std::endl; + EPR("write error: only {} bytes written instead of {}\n", + ret, length); break; } } diff --git a/src/cam/image.cpp b/src/cam/image.cpp index fe2cc6da..73bcf915 100644 --- a/src/cam/image.cpp +++ b/src/cam/image.cpp @@ -9,11 +9,14 @@ #include <assert.h> #include <errno.h> -#include <iostream> #include <map> #include <string.h> #include <sys/mman.h> #include <unistd.h> +#include <fmt/core.h> + +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) using namespace libcamera; @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode if (plane.offset > length || plane.offset + plane.length > length) { - std::cerr << "plane is out of buffer: buffer length=" - << length << ", plane offset=" << plane.offset - << ", plane length=" << plane.length - << std::endl; + EPR("plane is out of buffer: buffer length={}, plane offset={}, plane length={}\n", + length, plane.offset, plane.length); return nullptr; } size_t &mapLength = mappedBuffers[fd].mapLength; @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const FrameBuffer *buffer, MapMode MAP_SHARED, fd, 0); if (address == MAP_FAILED) { int error = -errno; - std::cerr << "Failed to mmap plane: " - << strerror(-error) << std::endl; + EPR("Failed to mmap plane: {}\n", + strerror(-error)); return nullptr; } diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp index 7add81a6..823b75e4 100644 --- a/src/cam/kms_sink.cpp +++ b/src/cam/kms_sink.cpp @@ -10,10 +10,13 @@ #include <array> #include <algorithm> #include <assert.h> -#include <iostream> #include <memory> #include <stdint.h> #include <string.h> +#include <fmt/core.h> + +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) #include <libcamera/camera.h> #include <libcamera/formats.h> @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName) if (!connector_) { if (!connectorName.empty()) - std::cerr - << "Connector " << connectorName << " not found" - << std::endl; + EPR("Connector {} not found\n", connectorName); else - std::cerr << "No connected connector found" << std::endl; + EPR("No connected connector found\n"); return; } @@ -119,7 +120,7 @@ int KMSSink::configure(const libcamera::CameraConfiguration &config) mode.vdisplay == cfg.size.height; }); if (iter == modes.end()) { - std::cerr << "No mode matching " << cfg.size << std::endl; + EPR("No mode matching {}\n", cfg.size); return -EINVAL; } @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const libcamera::PixelFormat &format) { const int ret = selectPipeline(format); if (ret) { - std::cerr - << "Unable to find display pipeline for format " - << format << std::endl; - + EPR("Unable to find display pipeline for format {}\n", format); return ret; } - std::cout - << "Using KMS plane " << plane_->id() << ", CRTC " << crtc_->id() - << ", connector " << connector_->name() - << " (" << connector_->id() << ")" << std::endl; + PR("Using KMS plane {}, CRTC {}, connector {} ({})\n", + plane_->id(), crtc_->id(), connector_->name(), connector_->id()); return 0; } @@ -228,9 +224,8 @@ int KMSSink::start() ret = request->commit(DRM::AtomicRequest::FlagAllowModeset); if (ret < 0) { - std::cerr - << "Failed to disable CRTCs and planes: " - << strerror(-ret) << std::endl; + EPR("Failed to disable CRTCs and planes: {}\n", + strerror(-ret)); return ret; } @@ -250,9 +245,7 @@ int KMSSink::stop() int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset); if (ret < 0) { - std::cerr - << "Failed to stop display pipeline: " - << strerror(-ret) << std::endl; + EPR("Failed to stop display pipeline: {}\n", strerror(-ret)); return ret; } @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request *camRequest) if (!queued_) { int ret = drmRequest->commit(flags); if (ret < 0) { - std::cerr - << "Failed to commit atomic request: " - << strerror(-ret) << std::endl; + EPR("Failed to commit atomic request: {}\n", + strerror(-ret)); /* \todo Implement error handling */ } diff --git a/src/cam/main.cpp b/src/cam/main.cpp index c7f664b9..03615dc9 100644 --- a/src/cam/main.cpp +++ b/src/cam/main.cpp @@ -6,10 +6,9 @@ */ #include <atomic> -#include <iomanip> -#include <iostream> #include <signal.h> #include <string.h> +#include <fmt/core.h> #include <libcamera/libcamera.h> #include <libcamera/property_ids.h> @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv) ret = cm_->start(); if (ret) { - std::cout << "Failed to start camera manager: " - << strerror(-ret) << std::endl; + fmt::print("Failed to start camera manager: {}\n", -ret); return ret; } @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[]) void CamApp::cameraAdded(std::shared_ptr<Camera> cam) { - std::cout << "Camera Added: " << cam->id() << std::endl; + fmt::print("Camera Added: {}\n", cam->id()); } void CamApp::cameraRemoved(std::shared_ptr<Camera> cam) { - std::cout << "Camera Removed: " << cam->id() << std::endl; + fmt::print("Camera Removed: {}\n", cam->id()); } void CamApp::captureDone() @@ -193,11 +191,11 @@ int CamApp::run() /* 1. List all cameras. */ if (options_.isSet(OptList)) { - std::cout << "Available cameras:" << std::endl; + fmt::print("Available cameras:\n"); unsigned int index = 1; for (const std::shared_ptr<Camera> &cam : cm_->cameras()) { - std::cout << index << ": " << cameraName(cam.get()) << std::endl; + fmt::print("{}: {}\n", cameraName(cam.get()), index); index++; } } @@ -215,12 +213,12 @@ int CamApp::run() index, camera.children()); if (!session->isValid()) { - std::cout << "Failed to create camera session" << std::endl; + fmt::print("Failed to create camera session\n"); return -EINVAL; } - std::cout << "Using camera " << session->camera()->id() - << " as cam" << index << std::endl; + fmt::print("Using camera{} as cam{}\n", + session->camera()->id(), index); session->captureDone.connect(this, &CamApp::captureDone); @@ -250,7 +248,7 @@ int CamApp::run() ret = session->start(); if (ret) { - std::cout << "Failed to start camera session" << std::endl; + fmt::print("Failed to start camera session\n"); return ret; } @@ -259,8 +257,8 @@ int CamApp::run() /* 5. Enable hotplug monitoring. */ if (options_.isSet(OptMonitor)) { - std::cout << "Monitoring new hotplug and unplug events" << std::endl; - std::cout << "Press Ctrl-C to interrupt" << std::endl; + fmt::print("Monitoring new hotplug and unplug events\n"); + fmt::print("Press Ctrl-C to interrupt\n"); cm_->cameraAdded.connect(this, &CamApp::cameraAdded); cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved); @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera) void signalHandler([[maybe_unused]] int signal) { - std::cout << "Exiting" << std::endl; + fmt::print("Exiting"); CamApp::instance()->quit(); } diff --git a/src/cam/meson.build b/src/cam/meson.build index 5bab8c9e..2b47383d 100644 --- a/src/cam/meson.build +++ b/src/cam/meson.build @@ -7,6 +7,8 @@ if not libevent.found() subdir_done() endif +libfmt_dep = dependency('fmt') + cam_enabled = true cam_sources = files([ @@ -25,7 +27,7 @@ cam_cpp_args = [] libdrm = dependency('libdrm', required : false) if libdrm.found() - cam_cpp_args += [ '-DHAVE_KMS' ] + cam_cpp_args += [ '-DHAVE_KMS', ] cam_sources += files([ 'drm.cpp', 'kms_sink.cpp' @@ -38,6 +40,7 @@ cam = executable('cam', cam_sources, libcamera_public, libdrm, libevent, + libfmt_dep, ], cpp_args : cam_cpp_args, install : true) diff --git a/src/cam/options.cpp b/src/cam/options.cpp index 4f7e8691..c9979385 100644 --- a/src/cam/options.cpp +++ b/src/cam/options.cpp @@ -7,9 +7,11 @@ #include <assert.h> #include <getopt.h> -#include <iomanip> -#include <iostream> #include <string.h> +#include <fmt/core.h> + +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) #include "options.h" @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const char *arguments) continue; if (optionsMap_.find(key) == optionsMap_.end()) { - std::cerr << "Invalid option " << key << std::endl; + EPR("Invalid option {}\n", key); return options; } OptionArgument arg = optionsMap_[key].argument; if (value.empty() && arg == ArgumentRequired) { - std::cerr << "Option " << key << " requires an argument" - << std::endl; + EPR("Option {} requires an argument\n", key); return options; } else if (!value.empty() && arg == ArgumentNone) { - std::cerr << "Option " << key << " takes no argument" - << std::endl; + EPR("Option {} takes no argument\n", key); return options; } const Option &option = optionsMap_[key]; if (!options.parseValue(key, option, value.c_str())) { - std::cerr << "Failed to parse '" << value << "' as " - << option.typeName() << " for option " << key - << std::endl; + EPR("Failed to parse '{}' as {} for option {}\n", + value, option.typeName(), key); return options; } } @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent) argument += "]"; } - std::cerr << std::setw(indent) << argument; + EPR("{:{}}", argument, indent); for (const char *help = option.help, *end = help; end;) { end = strchr(help, '\n'); if (end) { - std::cerr << std::string(help, end - help + 1); - std::cerr << std::setw(indent) << " "; + EPR(std::string(help, end - help + 1)); + EPR("{:{}}", "", indent); help = end + 1; } else { - std::cerr << help << std::endl; + EPR("{}\n", help); } } } @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv) if (c == '?' || c == ':') { if (c == '?') - std::cerr << "Invalid option "; + EPR("Invalid option "); else - std::cerr << "Missing argument for option "; - std::cerr << argv[optind - 1] << std::endl; + EPR("Missing argument for option "); + EPR("{}\n", argv[optind - 1]); usage(); return options; @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc, char **argv) } if (optind < argc) { - std::cerr << "Invalid non-option argument '" << argv[optind] - << "'" << std::endl; + EPR("Invalid non-option argument '{}'\n", argv[optind]); usage(); return options; } @@ -992,14 +990,9 @@ void OptionsParser::usage() indent = (indent + 7) / 8 * 8; - std::cerr << "Options:" << std::endl; - - std::ios_base::fmtflags f(std::cerr.flags()); - std::cerr << std::left; + EPR("Options:\n"); usageOptions(options_, indent); - - std::cerr.flags(f); } void OptionsParser::usageOptions(const std::list<Option> &options, @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const std::list<Option> &options, if (option.isArray) argument += " ..."; - std::cerr << std::setw(indent) << argument; + EPR("{:{}}", argument, indent); - for (const char *help = option.help, *end = help; end; ) { + for (const char *help = option.help, *end = help; end;) { end = strchr(help, '\n'); if (end) { - std::cerr << std::string(help, end - help + 1); - std::cerr << std::setw(indent) << " "; + EPR(std::string(help, end - help + 1)); + EPR("{:{}}", "", indent); help = end + 1; } else { - std::cerr << help << std::endl; + EPR("{}\n", help); } } @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const std::list<Option> &options, return; for (const Option *option : parentOptions) { - std::cerr << std::endl << "Options valid in the context of " - << option->optionName() << ":" << std::endl; + EPR("\nOptions valid in the context of {}:\n", + option->optionName()); usageOptions(option->children, indent); } } @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option &option, const char *arg, std::tie(options, error) = childOption(option.parent, options); if (error) { - std::cerr << "Option " << option.optionName() << " requires a " - << error->optionName() << " context" << std::endl; + EPR("Option {} requires a {} context\n", + option.optionName(), error->optionName()); return false; } if (!options->parseValue(option.opt, option, arg)) { - std::cerr << "Can't parse " << option.typeName() - << " argument for option " << option.optionName() - << std::endl; + EPR("Can't parse {} argument for option {}\n", + option.typeName(), option.optionName()); return false; } diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp index 150bd27c..666862eb 100644 --- a/src/cam/stream_options.cpp +++ b/src/cam/stream_options.cpp @@ -6,7 +6,10 @@ */ #include "stream_options.h" -#include <iostream> +#include <fmt/core.h> + +#define PR(...) fmt::print(__VA_ARGS__) +#define EPR(...) fmt::print(stderr, __VA_ARGS__) using namespace libcamera; @@ -30,8 +33,7 @@ KeyValueParser::Options StreamKeyValueParser::parse(const char *arguments) if (options.valid() && options.isSet("role") && !parseRole(&role, options)) { - std::cerr << "Unknown stream role " - << options["role"].toString() << std::endl; + EPR("Unknown stream role {}\n", options["role"].toString()); options.invalidate(); } @@ -64,7 +66,7 @@ int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, const OptionValue &values) { if (!config) { - std::cerr << "No configuration provided" << std::endl; + EPR("No configuration provided\n"); return -EINVAL; } @@ -75,12 +77,8 @@ int StreamKeyValueParser::updateConfiguration(CameraConfiguration *config, const std::vector<OptionValue> &streamParameters = values.toArray(); if (config->size() != streamParameters.size()) { - std::cerr - << "Number of streams in configuration " - << config->size() - << " does not match number of streams parsed " - << streamParameters.size() - << std::endl; + EPR("Number of streams in configuration {} does not match number of streams parsed {}\n", + config->size(), streamParameters.size()); return -EINVAL; }
This is just a conversation starter, not for merging. I really like libfmt, and I really hate the C++ stream-based string formatting. libfmt is the first library I add to any new C++ project I make. You can find more information about libfmt from: https://github.com/fmtlib/fmt https://fmt.dev/latest/index.html This patch is just a crude conversion with ugly macros to showcase what the formatting code might look like. libfmt pages suggest that libfmt would be faster and smaller (exe size) than iostreams, but for the size it didn't seem to be true in cam's case as the tests below show. However, simple prints did reduce the exe size, but the few more complex ones increased the size. Size tests with gcc 11.2.0-19ubuntu1 - Without libfmt debug 3523400 debug lto 3269368 release 223056 release lto 172280 - With libfmt debug 4424256 debug lto 4143840 release 303952 release lto 252640 Above shows that cam's size clearly increases with libfmt. However, the increase really comes only from one case, the use of fmt::memory_buffer and std::back_inserter. Converting that code to use fmt::format() and naive string append gives: release with string append 233680 release lto with string append 186936 Also, if I add another use of fmt::memory_buffer and std::back_inserter to another file, I see much less increase in the size: release lto with two uses of memory_buffer, back_inserter 256736 Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> --- src/cam/camera_session.cpp | 105 ++++++++++++++++--------------------- src/cam/drm.cpp | 68 ++++++++---------------- src/cam/event_loop.cpp | 9 ++-- src/cam/file_sink.cpp | 31 +++++------ src/cam/image.cpp | 15 +++--- src/cam/kms_sink.cpp | 38 ++++++-------- src/cam/main.cpp | 28 +++++----- src/cam/meson.build | 5 +- src/cam/options.cpp | 66 ++++++++++------------- src/cam/stream_options.cpp | 18 +++---- 10 files changed, 165 insertions(+), 218 deletions(-)