Skip to content

Conversation

larshg
Copy link
Contributor

@larshg larshg commented Mar 1, 2025

I'm in deep water here, but it seems to work :)

Please come with suggestions, before spent too much time in the wrong way.

Could close #5589

@larshg larshg force-pushed the addconsoleredirect branch 10 times, most recently from a544354 to 9eb28fc Compare March 1, 2025 21:13
@larshg
Copy link
Contributor Author

larshg commented Mar 1, 2025

@mvieth do you have any idea to why there is linker errors with pcl::octree:OctreeKey::maxDepth ?

@mvieth
Copy link
Member

mvieth commented Mar 1, 2025

@mvieth do you have any idea to why there is linker errors with pcl::octree:OctreeKey::maxDepth ?

Must have something to do with the usage of maxDepth here: https://github.com/PointCloudLibrary/pcl/blob/master/octree/include/pcl/octree/impl/octree_base.hpp#L98
maxDepth is a static const member, so maybe something related to ODR-use? I have to look at your changes in detail first before I can make a more specific guess.

@larshg
Copy link
Contributor Author

larshg commented Mar 1, 2025

Ahh, its in that print statement it fails... hmm, still odd though.

@larshg larshg force-pushed the addconsoleredirect branch 2 times, most recently from 6d1d0a4 to 44320b0 Compare March 2, 2025 19:44
@larshg
Copy link
Contributor Author

larshg commented Mar 2, 2025

Maybe pcl::console::LogRecorder should be a member of the Logger, to avoid creating a stringstream for each stream command - would that make sense?

@roncapat
Copy link
Contributor

roncapat commented Mar 3, 2025

Nice for me, even if I not like too much the following:

pcl::console::Logger::getInstance().setCallback(...)

because of the getInstance() call... is there a way to remove this indirection level?

Moreover, I think that the previous callback needs to be returned from the setCallaback() call to be able to restore it.

@larshg
Copy link
Contributor Author

larshg commented Mar 3, 2025

Nice for me, even if I not like too much the following:

pcl::console::Logger::getInstance().setCallback(...)

because of the getInstance() call... is there a way to remove this indirection level?

Moreover, I think that the previous callback needs to be returned from the setCallaback() call to be able to restore it.

Somehow the callback needs to be saved - and having just a static global variable to a std::function, didn't seem right either. At least it was the best solution I found, also browsing other logging frameworks.

return instance;
}

////////////////////////////////////////////////////////////////////////////////
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

template <typename Functor>
void pcl::console::Logger::setCallback(Functor&& callback){
      getInstance().setCallback(std::move(callback));
}

DISCLAIMER: draft, untested code.
@larshg wouldn't this work? Would the template be a problem there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want a free function like:


    template <typename Functor>
    void
    setCallback(Functor&& callback)
    {
      Logger::getInstance().setCallback(std::move(callback));
    }

so you can call:
pcl::console::setCallback(...);

Yes, thats possible.

@roncapat
Copy link
Contributor

roncapat commented Mar 3, 2025

Moreover, why generic template "functor" instead of std::function?

@larshg
Copy link
Contributor Author

larshg commented Mar 3, 2025

Else I couldn't get it to work with lambdas - but maybe I did it wrong 😄

@larshg
Copy link
Contributor Author

larshg commented Mar 3, 2025

To restore normal logging again, you can just set the callback to a nullptr.

@aurelienrb
Copy link

I had a quick look and it looks promising to me, thanks for your work

Copy link
Member

@mvieth mvieth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we are targeting PCL 1.16.0, due to likely ABI changes?

Is there a way to easily reset to the default behaviour, after calling setCallback? Edit: I just read your other comment: set callback to a nullptr. Please document this in the code somewhere.

}

LogRecorder&
operator<<(std::ostream& (std::ostream&))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one looks strange, what is it for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was for handling std::endl if I remember correctly - that apparently need this operator.

if (pcl::io::loadPCDFile (pcd_filename, input_pointcloud2))
{
PCL_ERROR ("ERROR: Could not read input point cloud %s.\n", pcd_filename.c_str ());
PCL_ERROR ("ERROR: Could not read input point cloud %s.\n", pcd_filename);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does .c_str() still work? But is not necessary any more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems it still is necessary. See https://godbolt.org/z/zaca3aGK3 - MSVC output correctly, but GCC output random stuff.


/* \brief maximum depth that can be addressed */
static const unsigned char maxDepth =
static_cast<unsigned char>(sizeof(uindex_t) * 8);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to keep this and deprecate it? Same for max_sample_checks_.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess so - not sure if it will do linking errors downstream though.

{ \
pcl::console::LogRecorder rec; \
rec << ARGS; \
pcl::console::LogRecord entry{pcl::console::LEVEL, rec.to_string()}; \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be a way to tell the stringstream of LogRecorder to directly operate on the message string of the LogRecord? Otherwise there are multiple allocations and copies here, I think.

@larshg larshg force-pushed the addconsoleredirect branch from 4248763 to 5384843 Compare March 25, 2025 15:33
@larshg larshg force-pushed the addconsoleredirect branch from 448c188 to 746959d Compare March 25, 2025 15:53
@larshg larshg added this to the pcl-1.16.0 milestone Mar 26, 2025
@larshg larshg requested a review from Copilot March 26, 2025 18:18
Copilot

This comment was marked as outdated.

@larshg larshg requested a review from Copilot August 9, 2025 16:41
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds the ability to redirect PCL logging to a callable function, which addresses the need for programmatic access to logging output rather than just console printing. This change allows users to capture and handle log messages in custom ways.

  • Introduces a callback-based logging system with LogRecord structure
  • Refactors existing print functions to use templates and std::string instead of C-style variadic arguments
  • Replaces deprecated static member variables with static getter functions

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
common/include/pcl/console/print.h Adds Logger class with callback support and converts print functions to templates
common/src/print.cpp Implements Logger singleton and callback-based printing logic
test/common/test_console.cpp New test file for validating logging functionality and callback behavior
test/common/CMakeLists.txt Adds test registration for console testing
sample_consensus/include/pcl/sample_consensus/sac_model.h Replaces deprecated static member with getter function
octree/include/pcl/octree/octree_key.h Replaces deprecated static member with getter function
octree/include/pcl/octree/impl/octree_pointcloud.hpp Updates to use new getter function
octree/include/pcl/octree/impl/octree_base.hpp Updates to use new getter function and adds missing includes
octree/include/pcl/octree/impl/octree2buf_base.hpp Updates to use new getter function
octree/src/octree_inst.cpp Adds missing include for octree_key.h
octree/include/pcl/octree/octree_impl.h Removes redundant include
tools/train_linemod_template.cpp Updates print calls to use std::string instead of .c_str()
examples/segmentation/example_lccp_segmentation.cpp Simplifies logging by removing stringstream usage
examples/segmentation/example_cpc_segmentation.cpp Simplifies logging by removing stringstream usage

*/
template <typename... Args>
std::string
to_string(const std::string fmt_str, ...)
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variadic template function to_string declares variadic arguments with ... but doesn't use them in the parameter list. This should be to_string(const std::string& fmt_str, Args&&... args) to properly accept the template arguments.

Copilot uses AI. Check for mistakes.

va_list ap;
while (true) {
formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
std::strcpy(&formatted[0], fmt_str.c_str());
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to std::strcpy is copying the format string over the allocated buffer, but then vsnprintf is called with the same format string. This strcpy call is unnecessary and potentially dangerous as it doesn't respect buffer bounds. Remove this line.

Suggested change
std::strcpy(&formatted[0], fmt_str.c_str());
// Removed unnecessary and potentially dangerous strcpy

Copilot uses AI. Check for mistakes.

// Get a second point which is different than the first
samples.resize (getSampleSize ());
for (unsigned int iter = 0; iter < max_sample_checks_; ++iter)
for (unsigned int iter = 0; iter < getMaxSampleSize(); ++iter)
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loop condition has changed from max_sample_checks_ to getMaxSampleSize(). However, getMaxSampleSize() returns the maximum sample checks (1000), while the function name suggests it returns a sample size. This creates confusion and potential logical errors. The function should be named getMaxSampleChecks() to match its purpose.

Copilot uses AI. Check for mistakes.

pcl::console::setVerbosityLevel(pcl::console::L_VERBOSE);

PCL_ALWAYS_STREAM("Always_stream\n");
PCL_ERROR_STREAM("Error_stream\n")
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the statement.

Suggested change
PCL_ERROR_STREAM("Error_stream\n")
PCL_ERROR_STREAM("Error_stream\n");

Copilot uses AI. Check for mistakes.


class LogRecorder {
public:
LogRecorder(std::string& string) : strstream(string) { }
Copy link

Copilot AI Aug 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor is trying to initialize strstream with a std::string&, but strstream is declared as std::stringstream. This will cause a compilation error. The constructor should be LogRecorder(std::string& string) and the string should be stored as a reference to be updated later, or the design should be changed.

Suggested change
LogRecorder(std::string& string) : strstream(string) { }
LogRecorder(std::string& string) : target_string(string) { }
~LogRecorder() { target_string = strstream.str(); }

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[console] How can I redirect the log output?

4 participants