Displaying custom failure message in UnitTest++? - unit-testing

I've got a UnitTest++ test class, which allows me to test that a class is parsing some strings correctly. Before running the test, I create a fixture that contain several strings to be tested by various functions in a loop. It seems to work fine, but the problem is that, in case of error, UnitTest++ will always give me the same error line, so I won't know which string exactly is causing the issue.
For example, it will output:
"[UnitTest++] ..\trunk\tests\Test_ChineseUtil.cpp(46): error: Failure in ParsePinyinT: ChineseUtil::parsePinyinT(pinyinT) == pinyinN"
But that doesn't tell me which string is not being parsed correctly.
So what I would like is to set some custom error message when a test fails (in that particular case, I would give it the first item in my array). Basically, I need something like:
CHECK(theTest, "my error message")
Is there such function in UnitTest++? Or maybe there is a better way to do what I'm trying to do?
For information, here is the code of my class:
#include <third_party/unittest++/UnitTest++.h>
#include <Application.h>
#include <ChineseUtil.h>
using namespace hanzi;
namespace chineseUtilTests {
class PinyinFixture {
public:
PinyinFixture() {
ChineseUtil::initialize();
testData << "third tone" << QString::fromUtf8("wo3") << QString::fromUtf8("wǒ");
testData << "no tone" << QString::fromUtf8("wo") << QString::fromUtf8("wo");
testData << "second tone" << QString::fromUtf8("guo2") << QString::fromUtf8("guó");
testData << "first tone" << QString::fromUtf8("jia1") << QString::fromUtf8("jiā");
testData << "fifth tone" << QString::fromUtf8("jia5") << QString::fromUtf8("jia");
testData << "two dots" << QString::fromUtf8("nu:") << QString::fromUtf8("nü");
testData << "two dots and tone" << QString::fromUtf8("nu:3") << QString::fromUtf8("nǚ");
}
~PinyinFixture() {
}
QStringList testData;
};
TEST_FIXTURE(PinyinFixture, ParsePinyinN) {
for (int i = 0; i < testData.size(); i++) {
QString pinyinN = testData[i][1];
QString pinyinT = testData[i][2];
CHECK(ChineseUtil::parsePinyinN(pinyinN) == pinyinT);
}
}
TEST_FIXTURE(PinyinFixture, ParsePinyinT) {
for (int i = 0; i < testData.size(); i++) {
QString pinyinN = testData[i][1];
QString pinyinT = testData[i][2];
CHECK(ChineseUtil::parsePinyinT(pinyinT) == pinyinN);
}
}
} // chineseUtilTests

If your class has an equality operator, you should be able to, instead of typing
CHECK(something == something_else);
use
CHECK_EQUAL( something, something_else);
The fact that your classes allow a == makes me thing you can do this. If the test fails, you should get something along the lines of "Expected something but got something else".
If you need more information, there are a couple other things you can do.
One is to add your own custom output in the test. If you need to know the value of i specifically, you can add a printout of i before each CHECK. However, this output may be too lengthy for large loops, so you can add your check a second time.
CHECK(ChineseUtil::parsePinyinN(pinyinN) == pinyinT);
if ( ChineseUtil::parsePinyinN(pinyinN) != pinyinT )
{
cout << "Your own custom message " << i << endl;
}
Another possibility is to modify the source code of UnitTest++ itself, then recompile the library. While I don't know the exact steps to do what you want to do, I've modified UnitTest++/src/Checks.h to improve the output of CHECK_ARRAY2D_CLOSE so it was more readable.

I have not compiled or tested the following, but the gist of it is to copy the UnitTest++ CHECK macro and expand it to take a message parameter that is std::string and combine that with the string representing the stringified version of value.
#define CHECK_MESSAGE(value, message) \
do \
{ \
try { \
if (!UnitTest::Check(value)) \
UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), (std::string(#value)+message).c_str()); \
} \
catch (...) { \
UnitTest::CurrentTest::Results()->OnTestFailure(UnitTest::TestDetails(*UnitTest::CurrentTest::Details(), __LINE__), \
(std::string("Unhandled exception in CHECK(" #value)+message+std::string(", ")+message+std::string(")")).c_str()); \
} \
} while (0)

Related

Cant Use Ranges on VkLayerProperties?

I was trying out C++20 ranges while working on my Vulkan project, I decided I wanted to try to check if a layer was available before the client added it in my builder object.
I set up my layer properties vector like so:
//...
vkEnumerateInstanceLayerProperties(&amount_of_layers, nullptr);
std::vector<VkLayerProperties> layer_properties(amount_of_layers);
vkEnumerateInstanceLayerProperties(&amount_of_layers, layer_properties.data());
//...
If I print them out like so, it works just fine:
for(auto& current_properties : layer_properties)
std::cout << current_properties.layerName << "\n";
The problem comes when I tried to use ranges to filter them, this is not the original filter I tried to apply, but this has similar problems:
auto available_layers = layer_properties | std::views::filter(
[&](auto current_layer_properties) {
return std::string{current_layer_properties.layerName}.size() % 2 == 0;
}
);
I just need a count at this point, not to iterate so I just store it in available_layers.
However if I try to print out current_layer_properties.layerName I get the first few layer names, and then what looks like a bunch of data that is from the data segment of the Vulkan library binaries, it looks like JSON.
VK_LAYER_MESA_device_select
VK_LAYER_INTEL_nullhw
VK_LAYER_MESA_overlay
VK_LAYER_LUNARG_monitor
VK_LAYER_KHRONOS_synchronization2
VK_LAYER_LUNARG_api_dump
E_CREATE_NEW",
"label": "Log File Overwrite",
"description": "Specifies that log file initialization should overwrite an existing file when true, or append to an existing file when false.",
"type": "BOOL",
"default": true
}
]
}
]
}
]
}
//...
Same happens if I try to apply std::views::transform and turn layer_properties into a container/view of std::string the same thing happens.
Sometimes depending on my filter (I use a find to find a string in it, then omit the element if its not found) it goes on forever.
Am I doing something wrong with ranges? Or does this have something to do with Vulkan?
Does it have something to do with some sort of side effect these LunarG .gfxr/gfxrecon_capture log files I am seeing(the erroneous output looks related, but could be totally random)?
UPDATE[ 0 ]:
Someone in the comments requested I show how I print things, it has changed as I have debugged, but I will put it here (it also crashes without printing)
In the lambda for std::filter and/or std::transform I have put variations of the following:
std::cout << current_layer_properties.layerName << "\n";
std::cout << current_layer_properties << "\n"; //When I apply std::transform and turn it into a std::string or std::string_view
log<LogType::Debug>(std::cout, current_layer_properties);
Here is my log function
template<typename LogParameterType>
void log(
LogParameterType& log,
const std::string_view message,
const std::source_location location =
std::source_location::current()
)
{
if constexpr(DebugParameterConstant == true
&& LogParameterConstant == LogType::Debug)
return;
log << to_string(LogParameterConstant) << "::"
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ")::"
<< location.function_name() << ": "
<< message << "\n";
}
My debug layer is not working but here is the function it is suppose to run:
//If anyone can think of a better way to do this please let me know :)
void vulkan_log(
VkDebugUtilsMessageSeverityFlagBitsEXT vulkan_log_type,
VkDebugUtilsMessageTypeFlagsEXT message_type,
const VkDebugUtilsMessengerCallbackDataEXT* callback_data
)
{
LogType log_type = LogType::Unkown;
std::string message_type_string = to_string(message_type);
switch(vulkan_log_type)
{
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
{
std::cerr << message_type_string;
log<LogType::Diagnostic>(std::cerr, callback_data->pMessage);
break;
}
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
{
std::cerr << message_type_string;
log<LogType::Error>(std::cerr, callback_data->pMessage);
break;
}
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
{
std::cerr << message_type_string;
log<LogType::Warning>(std::cerr, callback_data->pMessage);
break;
}
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
{
std::cout << message_type_string;
log<LogType::Note>(std::cout, callback_data->pMessage);
break;
}
default:
break;
}
}
Thanks!
TFB

c++ gtest print additional information in the end of a test when and only when the test fails

I want to do somethink like this:
TEST(MyTestFixture, printAfterExpectationFailure)
{
const string request("bring me tea");
const string&& response = sendRequestAndGetResponse(request);
checkResponseWithExpectarions1(response);
checkResponseWithExpectarions2(response);
checkResponseWithExpectarions3(response);
checkResponseWithExpectarions4(response);
if (anyOfExpectsFailed())
cout << "Request: " << request << "\nresponse: " << response << endl;
}
TEST(MyTestFixture, printAfterAssertionFailure)
{
const string request("bring me tea");
const string&& response = sendRequestAndGetResponse(request);
doWhenFailure([&request, &response]()
{
cout << "Request: " << request << "\nresponse: " << response << endl;
});
checkResponseWithAssertion1(response);
checkResponseWithAssertion2(response);
checkResponseWithAssertion3(response);
checkResponseWithAssertion4(response);
}
I want to print some additional information when and only when expectations/assertions failures.
I know that I can do something like this:
#define MY_ASSERT_EQ(lhr, rhs, message) if(lhs != rhs) ASSERT_EQ(lhs, rhs) << message
but this kind of solution is not comfortable because:
I check twice
I use preprocessor so it can take some time to find
bug.
The solution is hard to use when functions are really nested.
When many EXPECTations failure it would print message many times.
It is necessairy to redefine macro for all kind of checking
the fact that doing what you want to do is difficult is actually a test code smell. In particular, these two tests (1) do too much and (2) are unreadable, in the sense that they do not describe what the unit under test does.
I recommend two readings: Unit Tests are FIRST and the book Modern C++ Programming with Test-Driven Development.
Instead of trying to call 4 functions, each checking something, and then wondering how to print an error message in case of failure, I suggest the following:
ask yourself: "what am I testing here?" When you have an answer, use the answer to give a name to the test. If you cannot find an answer, it means (what I suspect) that the test does too much. Try to follow the "one assert per test" guideline and split the test accordingly.
in the same spirit, look at each of the 4 functions and try to give each a name. If you cannot, each function is checking too much. Split these functions.
Ask yourself if you really need expectations (as opposed to assertions). Often the only reason to have an EXPECT instead than an ASSERT is because the single test is doing too much. Split it.
At the end of this process, you should find yourself in a situation where your goal of printing additional information on test failure can be reached simply with something like:
ASSERT_THAT(Response, EQ("something")) << "Request: " << request;
Note: also if better that the starting point, I don't consider the above example good enough. The test name should be so good, so descriptive, that you would gain zero information by printing the value of request.
I realize this is a sort of philosophical answer; on the other hand it comes directly from my experience in attempting to write good, maintainable tests. Writing good tests requires the same care than writing good code, and it will pay off many times :-)
A non-ideological answer (based on information from all over the place):
QDebugTest.h
class QDebugTest : public ::testing::Test
{
public:
void SetUp() override;
void TearDown() override;
};
QDebugTest.cpp
static std::ostringstream qdebugString;
static void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
switch (type) {
case QtDebugMsg: qdebugString << "qDebug"; break;
case QtInfoMsg: qdebugString << "qInfo"; break;
case QtWarningMsg: qdebugString << "qWarning"; break;
case QtCriticalMsg: qdebugString << "qCritical"; break;
case QtFatalMsg: qdebugString << "qFatal"; break;
}
if (context.file) {
qdebugString << " (" << context.file << ":" << context.line ;
}
if (context.function) {
qdebugString << " " << context.function;
}
if(context.file || context.function) {
qdebugString << ")";
}
qdebugString << ": ";
qdebugString << msg.toLocal8Bit().constData();
qdebugString << "\n";
}
void QDebugTest::SetUp()
{
assert(qdebugString.str().empty());
qInstallMessageHandler(myMessageOutput);
}
void QDebugTest::TearDown()
{
qInstallMessageHandler(0);
if(!qdebugString.str().empty()) {
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
if (test_info->result()->Failed()) {
std::cout << std::endl << qdebugString.str();
}
qdebugString.clear();
}
}
Now derive your Fixture-class from QDebugTest instead of ::testing::Test.

Check if multiple strings are empty and print their name

Usually when i want to check if more input stored in a multiple strings are not empty i follow this simple approach:
std::string fieldA = "";
std::string fieldB = "";
std::string fieldC = "Hello";
Now, i can check for all:
if ( fieldA.empty() || fieldB.empty() || fieldC.empty() )
std::cout << "Oh oh.. one or more fields are empty << std::endl;
But it would be nice to know which fields are empty, then, i can write:
if ( fieldA.empty() )
std::cout << "fieldA is empty" << std::endl;
if ( fieldB.empty() )
std::cout << "fieldB is empty" << std::endl;
if ( fieldC.empty() )
std::cout << "fieldC is empty" << std::endl;
But in this way i can discover that fieldA is empty but not the fieldB and in this example i have only three fields, but with more fields?
What is the best practice to managing the control of many strings and locate the empty string?
PaulMcKenzies comment is the one you should follow. But assuming your example is an over simplification of your code, and you can't put all your variables in an array, I think you can be excused if you use a little macro to do stringification for you:
#define PRINT_IF_EMPTY(var) \
do { \
if (var.empty()) \
std::cout << #var " is empty" << std::endl; \
} while(0)
You can now replace your code with this:
PRINT_IF_EMPTY(fieldA);
PRINT_IF_EMPTY(fieldB);
PRINT_IF_EMPTY(fieldC);
You should use a separate FieldValidator class. With that, your code should look like this:
FieldValidator val;
val.requireNotEmpty("field1", field1);
val.requireNotEmpty("field2", field2);
The idea is that all the validation state is kept in one place. I'm sure such a class already exists somewhere since you are not the first to solve this task.
It's better to use container (std::vector, std::list, std::array e.t.c) for similar type of data.
Let's assume you have stored your std::string in std::vector.
std::vector<std::strting> data;
// Now you have to insert std::string in std::vector.
for( int i = 0; i < data.size(); i++) {
if(data[i].empty())
std::cout << "field" << (char)i+65 << " is empty \n";
}
You could use a for-range loop over an initializer list, if printing which field is empty is not mandatory and if you prefer the fields to be distinct variables :
for (const std::string str : {fieldA, fieldB, fieldC})
{
if (str.empty())
{
std::cout << "One or more fields are empty." << std::endl;
break; // Break out of the for loop
}
}

Accessing variable values within a macro

Some time ago, I made this beautiful assert macro for c and c++ programs
#define ASSERT(truthy, message) \
if (!(truthy)) \
{\
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << endl;\
}
Scatter ASSERT calls throughout your code, and it will warn you whenever the truthy value is not truthy! Very handy during development to remind you of potential mistakes.
ex
ASSERT(filesFound > 0, "Couldn't find any files, check your path!");
When filesFound is 0, the macro will print out
Couldn't find any files, check your path! on line 27 in file
openFiles.c. Check was filesFound > 0
Now what I want it to print, to give me even more relevant information, is the value of any variables passed into the truthy parameter. Like this
Couldn't find any files, check your path! on line 27 in file
openFiles.c. Check was filesFound > 0, filesFound is 0
This seems lisp-like territory, I wonder, is there any black magic c preprocessing that I can use to evaluate variables and functions to their values, without evaluating the truthy statement?
I assume to be disappointed.
An alternative solution which I've always used is to support varargs in the macro and then force the assert user to specify the relevant message / variables - it's a little bit of extra work each time, but on the plus side you can get exactly the formatting that you want and include information not available in the "truthy" bit, e.g:
#define ASSERT(truthy, message, ...) \
if (!(truthy)) \
{\
MyAssertHandler(__LINE__, __FILE__, #truthy, message, ##__VA_ARGS__);
}
Then you're handler is just a fairly standard var-arg function that can use e.g. vsnprintf to generate the message and output it, e.g. off the top of my head:
void MyAssertHandler(int line, const char* file, const char* expressionStr, const char* format, ...)
{
// Note: You probably want to use vsnprintf instead to first generate
// the message and then add extra info (line, filename, etc.) to
// the actual output
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
// Log to bug database, DebugBreak() if a debugger is attached, etc.
}
usage:
ASSERT(IsBlah(), "BlahBlah: x = %.2f, name = %s", GetX(), GetName());
I cannot imagine a way to do it... except by passing another parameter
#define ASSERT_PARAM(truthy, message, param) \
if (!(truthy)) \
{\
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << ", value was " << param << endl;\
}
You would use it that way:
ASSERT_PARAM(filesFound > 0, "Couldn't find any files, check your path!", filesFound);
getting:
Couldn't find any files, check your path! on line 27 in file openFiles.c. Check was filesFound > 0, value was 0
What you are trying to do sounds very complicated. I'm afraid in C++ it's not possible.
Technically what you are evaluating is a bool expression so you can pass it to a parser whenever the assertion fails. The parser then will build the expression tree, get the leaves (elements of the expression) and return them. The returned values then should be printed out. To do that you will need support for reflection which is actually not supported in C++ AFAIK.
Maybe not the dream solution, but you can pass whole statements to a macro.
#define ASSERT(trusty, action) if (!trusty) { action }
ASSERT(trusty, cout << a << b;)
ASSERT(trusty, printf("%d, %f\n", a, b);)
I think you can split up the truthy Expression like they do it in the first answer here and then you can probably print the individual values. But I'm not sure if it actually works.
The printing could then be resulved using a variadic template function
Perhaps you could compromise and only allow 2 variables and 1 operator in the assertion expression? If so, you could make an ad hoc solution like this:
#include <iostream>
#include <string>
#define STRINGIFY(x) #x
#define BIN_ASSERT(obj1, op, obj2, msg) \
if(!(obj1 op obj2)) \
{ \
std::cout << msg << " on line " << __LINE__ \
<< " in file " << __FILE__ \
<< "." << std::endl \
<< "Check was " \
<< STRINGIFY(obj1) STRINGIFY(op) STRINGIFY(obj2) \
<< "." << std::endl \
<< "Operator " << #obj1 << ": " << obj1 \
<< "." << std::endl \
<< "Operator " << #obj2 << ": " << obj2 \
<< "." << std::endl; \
}
int main (void)
{
int x = 2;
int y = 3;
std::string s1 = "hello";
std::string s2 = "world";
BIN_ASSERT(1, +, -1, "Value zero"); std::cout << std::endl;
BIN_ASSERT(x, ==, y, "Numbers not equal"); std::cout << std::endl;
BIN_ASSERT(s1, ==, s2, "Strings not equal"); std::cout << std::endl;
}
Output:
Value zero on line 30 in file test.c.
Check was 1+-1.
Operator 1: 1.
Operator -1: -1.
Numbers not equal on line 31 in file test.c.
Check was x==y.
Operator x: 2.
Operator y: 3.
Strings not equal on line 32 in file test.c.
Check was s1==s2.
Operator s1: hello.
Operator s2: world.
I wonder if having the macro take a message is really that useful. A failed assertion is a message to the developer that there is a bug in the code that caused an exceptional behaviour or put the program in an unacceptable state. The user has less to do with it (if they even have access to the source code).
The code below defines an ASSERT macro that takes a boolean expression, evaluates it and prints an informational message. The message contains a value that you've asked to inspect upon failing the assertion.
The macro, just like the standard assert() macro (in <cassert>) goes on to call abort() (from <cstdlib>) to cause an abnormal program termination. This is what you want, because the program entered a state in which it didn't know what more to do.
I'm using std::printf() here for brevity. You do whatever you want.
#include <cstdlib>
#include <cstdio>
#define ASSERT(value, inspect) \
if (!(value)) { \
std::printf("ASSERTION FAILED: '%s', %s is %d: %s#%s:%d\n", #value, \
#inspect, inspect, __func__, __FILE__, __LINE__); \
abort(); \
}
int foo() { return 42; }
int main()
{
// ...
ASSERT(foo() - 40 == 1, foo());
//...
}
Program run:
$ ./a.out
ASSERTION FAILED: 'foo() - 40 == 1', foo() is 42: main#prog.cc:16
Abort
It's not possible to do exactly what you ask for without adding more parameters to the macro. At some point you'll have to stop and realize that you're spending time on creating a text string that you do not want to see.
You need to build an expression 'grabber' / builder.
The macro would become something like:
#define ASSERT_PARAM(truthy, message, param) \
if (!(truthy)) \
{\
Grabber g;
g << #truthy; // grab expression as string
g % truthy; // grab expression and values
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << ", value was " << param << endl;\
cout << g; \
}
What does Grabber do?
It is a bunch of crazy C++ that builds up an expression. It would overload every operator to 'grab' the params of the operator. Every operator returns a reference to the grabber, so it can grab the next operator. ie
Grabber g;
g % filesFound > 0;
Since % (and * and /) have high precedence, the above parses like:
((g % filesFound) > 0)
If template<typename T> Grabber::operator%(T const & val) just records (or prints) the value passed in (ie filesFound), and - importantly - returns itself (g) so that it becomes part of the next expression: ie it becomes g > 0. Causing template<typename T> Grabber::operator>(T const & val) to be called, and > 0 to be recorded.
Then cout << g can spew out everything grabbed.
As mentioned above "It is possible — the Catch library does it. But it’s hellishly difficult".
P.S. you should wrap your macro in a do ... while 0 like this:
#define ASSERT_PARAM(truthy, message, param) \
do \
{ \
if (!(truthy)) \
{\
cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << ", value was " << param << endl;\
cout << g; \
} \
} while (0)
What you have currently means that this is valid code:
ASSERT(foo != 0)
else
{
}
And this is NOT valid code:
if (foo != nullptr)
ASSERT(foo->bar != nullptr);
else
x = 10;
Surprisingly, I solved a similar problem before, but I'm not sure if it could help you in this case.
The original solution was proposed by Andrei Alexandrescu in the article Enhancing Assertions, and with no question, relying on some macro tricks.
This amazing facility can be used as the following:
string s1, s2;
...
SMART_ASSERT(s1.empty() && s2.empty())(s1)(s2);
And if something goes wrong, the message would be displayed
Assertion failed in matrix.cpp: 879412:
Expression: 's1.empty() && s2.empty()'
Values: s1 = "Wake up, Neo"
s2 = "It's time to reload."
Be noted that, the SMART_ASSERT can capture infinite variables, theoretically.
For implementation details, please check out the article.

Has anyone been able to use libsensors properly?

Long story short I am trying to write an application that can check cpu temperatures. Using the libsensors(3) man pages I've been able to at least get the libsensors_version number. As of now, here is my code:
#include <sensors/sensors.h>
#include "SensorData.h"
#include <string>
#include <sstream>
using namespace std;
SensorData::SensorData()
{
sensors_init(NULL);
}
SensorData::~SensorData()
{
sensors_cleanup();
}
string SensorData::GetVersion()
{
ostringstream Converter;
Converter<<"Version: "<<libsensors_version;
return Converter.str();
}
void SensorData::FetchTemp()
{
//sensors_get_value()
}
With the man pages I know that sensors_get_value expects
const sensors_chip_name *name
int subfeat_nr
double *value
to be passed to it. The problem is I have no idea what those are exactly. Just about every function in the documentation has this problem. They all expect vague things I don't know how to supply.
So here is the bulk of the question: Does anyone have any working examples of this library I could look at? Or at the very least does anyone know how to give these functions the values they need?
EDIT:
Since no one seems to know much about this library, does anyone know of a different way to get temperatures?
You can find out how to use the API by browsing the source code. The code for the sensors program isn't too complex to follow.
To get you started, here's a quick function that:
enumerates all the chips
enumerates all their features
prints the values of their readable subfeatures
You can just add it to your existing skeleton class as-is.
(This code is for demo purposes only, not tested thoroughly at all.)
void SensorData::FetchTemp()
{
sensors_chip_name const * cn;
int c = 0;
while ((cn = sensors_get_detected_chips(0, &c)) != 0) {
std::cout << "Chip: " << cn->prefix << "/" << cn->path << std::endl;
sensors_feature const *feat;
int f = 0;
while ((feat = sensors_get_features(cn, &f)) != 0) {
std::cout << f << ": " << feat->name << std::endl;
sensors_subfeature const *subf;
int s = 0;
while ((subf = sensors_get_all_subfeatures(cn, feat, &s)) != 0) {
std::cout << f << ":" << s << ":" << subf->name
<< "/" << subf->number << " = ";
double val;
if (subf->flags & SENSORS_MODE_R) {
int rc = sensors_get_value(cn, subf->number, &val);
if (rc < 0) {
std::cout << "err: " << rc;
} else {
std::cout << val;
}
}
std::cout << std::endl;
}
}
}
}
The Gnome panel Sensors applet works with libsensors (and other backends); the full sources are available from Sourceforge, here: http://sensors-applet.sourceforge.net/index.php?content=source
… in particular, the libsensors plug-in looks fairly legible… I believe this should be a usable gitweb link straight to that code: http://sensors-applet.git.sourceforge.net/git/gitweb.cgi?p=sensors-applet/sensors-applet;a=blob;f=plugins/libsensors/libsensors-plugin.c;h=960c19f4c36902dee4e20b690f2e3dfe6c715279;hb=HEAD
Your code should looks like this:
/* Read /etc/sensors.d to get the names or use code in above post */
std::string chip_name = "CHIP_NAME-*";
/* Here you get the path to the chip you want to read */
int rc;
sensors_chip_name name;
rc = sensors_parse_chip_name(chip_name.c_str(), &name);
/* Check rc != 0 */
/* Here you get the internal structure */
int nr = 0; //Here I silently assume you have only one chip to read
const sensors_chip_name* p_chip;
p_chip = sensors_get_detected_chips(&name, &nr);
/* Check p_chip != 0 */
/* Now you read the value - this you can repeat in some for/while cycle */
double val;
/* Replace the "1" with the feature you want to read */
rc = sensors_get_value(p_chip, 1, &val);
std::cout << "Now I can use sensors library " << val << std::endl;
Hope it helps despite the fact it is not copy/paste solution.
You can obtain the const sensors_chip_name* p_chip; from the code above post as well.
I believe the problem is in fact the const sensors_chip_name MUST be returned and filled by sensors library.