admin管理员组文章数量:1404595
As a minimal example (this is part of a larger project), I have the following three files in a project with the following structure:
.
├── src
│ ├── binary_search.cpp
└── test
├── CMakeLists.txt
├── build
└── test_binary_search.cpp
binary_search.cpp:
#include <cstdint>
template <typename T> T binary_search(T A[], uint8_t len, T nu) {
uint8_t low = 0;
uint8_t high = len - 1;
while (low <= high) {
uint8_t mid = low + (high - low) / 2;
if (nu == A[mid])
return mid;
else if (nu > A[mid])
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
test_binary_search.cpp:
#include <boost/test/data/test_case.hpp>
#include <boost/test/included/unit_test.hpp>
#define BOOST_TEST_MODULE test_algorithms
BOOST_AUTO_TEST_SUITE(test_search)
int32_t input_arrs[4][8] = {{26, 31, 41, 41, 58, 59, 101, 104},
{26, 31, 41, 41, 58, 59, 101, 104},
{1, 4, 5, 7, 19, 28, 45, 92},
{1, 4, 5, 7, 19, 28, 45, 92}};
int32_t input_vals[] = {31, 32, 92, 101};
int32_t expected_vals[] = {1, -1, 7, -1};
auto test_cases = boost::unit_test::data::make(input_arrs) ^
boost::unit_test::data::make(input_vals) ^
boost::unit_test::data::make(expected_vals);
BOOST_DATA_TEST_CASE(test_binary_search, test_cases, input_arr, nu, exp) {
auto obs = binary_search(input_arr, 8, nu);
BOOST_ASSERT(obs == exp);
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++20")
project(test_algorithms)
set(SOURCES
../src/binary_search.cpp
test_binary_search.cpp
)
find_package(Boost 1.87.0 REQUIRED COMPONENTS unit_test_framework)
if (Boost_FOUND)
enable_testing()
include_directories(${Boost_INCLUDE_DIRS})
add_executable(test_binary_search ${SOURCES})
target_include_directories(test_binary_search PRIVATE ${Boost_INCLUDE_DIRS})
add_test(NAME test_binary_search COMMAND test_binary_search)
endif()
I am attempting to compile this on a MacBook Pro M3 Pro running MacOS Sequoia 15.3.1 using the following commands:
cmake -S . -B build
cmake --build build
after which I receive a very long error message. I suspect that the problem might be
/Users/graham/dev/cpp_experiments/test/test_binary_search.cpp:20:14: error: use of undeclared identifier 'binary_search'
20 | auto obs = binary_search(input_arr, 8, nu);
which may in turn be caused by
/opt/homebrew/include/boost/test/data/test_case.hpp:89:16: error: reference to type 'const const int[8]' could not bind to an lvalue of type 'const int'
89 | return *value;
I'm not sure where/how I'm casting to type const int
in this code; are the Boost macros are performing this conversion? I'm struggling to understand the recommended way to use boost::unit_test::data::make
to zip up the parameterized test data. Any help in how to better structure this code to create a parameterized test suite would be greatly appreciated!
As a minimal example (this is part of a larger project), I have the following three files in a project with the following structure:
.
├── src
│ ├── binary_search.cpp
└── test
├── CMakeLists.txt
├── build
└── test_binary_search.cpp
binary_search.cpp:
#include <cstdint>
template <typename T> T binary_search(T A[], uint8_t len, T nu) {
uint8_t low = 0;
uint8_t high = len - 1;
while (low <= high) {
uint8_t mid = low + (high - low) / 2;
if (nu == A[mid])
return mid;
else if (nu > A[mid])
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
test_binary_search.cpp:
#include <boost/test/data/test_case.hpp>
#include <boost/test/included/unit_test.hpp>
#define BOOST_TEST_MODULE test_algorithms
BOOST_AUTO_TEST_SUITE(test_search)
int32_t input_arrs[4][8] = {{26, 31, 41, 41, 58, 59, 101, 104},
{26, 31, 41, 41, 58, 59, 101, 104},
{1, 4, 5, 7, 19, 28, 45, 92},
{1, 4, 5, 7, 19, 28, 45, 92}};
int32_t input_vals[] = {31, 32, 92, 101};
int32_t expected_vals[] = {1, -1, 7, -1};
auto test_cases = boost::unit_test::data::make(input_arrs) ^
boost::unit_test::data::make(input_vals) ^
boost::unit_test::data::make(expected_vals);
BOOST_DATA_TEST_CASE(test_binary_search, test_cases, input_arr, nu, exp) {
auto obs = binary_search(input_arr, 8, nu);
BOOST_ASSERT(obs == exp);
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++20")
project(test_algorithms)
set(SOURCES
../src/binary_search.cpp
test_binary_search.cpp
)
find_package(Boost 1.87.0 REQUIRED COMPONENTS unit_test_framework)
if (Boost_FOUND)
enable_testing()
include_directories(${Boost_INCLUDE_DIRS})
add_executable(test_binary_search ${SOURCES})
target_include_directories(test_binary_search PRIVATE ${Boost_INCLUDE_DIRS})
add_test(NAME test_binary_search COMMAND test_binary_search)
endif()
I am attempting to compile this on a MacBook Pro M3 Pro running MacOS Sequoia 15.3.1 using the following commands:
cmake -S . -B build
cmake --build build
after which I receive a very long error message. I suspect that the problem might be
/Users/graham/dev/cpp_experiments/test/test_binary_search.cpp:20:14: error: use of undeclared identifier 'binary_search'
20 | auto obs = binary_search(input_arr, 8, nu);
which may in turn be caused by
/opt/homebrew/include/boost/test/data/test_case.hpp:89:16: error: reference to type 'const const int[8]' could not bind to an lvalue of type 'const int'
89 | return *value;
I'm not sure where/how I'm casting to type const int
in this code; are the Boost macros are performing this conversion? I'm struggling to understand the recommended way to use boost::unit_test::data::make
to zip up the parameterized test data. Any help in how to better structure this code to create a parameterized test suite would be greatly appreciated!
1 Answer
Reset to default 1Apparently the element types need to be movable, which isn't true for T(&)[N]
. You could replace it with std::array
.
Next you will find that you need to make the type printable.
Aside from this, there are numerous issues with the code:
binary_search.cpp
defines a template. Its definition MUST be available at place of invocation, so it has to be an include.I'd suggest renaming
binary_search.cpp
tobinary_search.hpp
or to split into a header/source where all template definitions reside in the header (Why can templates only be implemented in the header file?)binary_search
collides with a standard library algorithm name. This can (will!) lead to surprises when you least expect it (e.g. if you pass thestd::array<>
directly, ADL will findstd::binary_search
in favor of your own.I suggest renaming your algorithm to
my_binary_search
or making it a member of a namespace so you can fully qualify it for clarityYour interface takes
T A[]
which is equivalent toT* A
. Also, it's an antipattern in C++ to pass raw pointers with separate size. Consider specifying it asstd::span<T>
instead with no overhead. Now you don't have to do the awkward, error-prone copy to a temporary array eitherYour interface hard-codes
uint8_t
forlen
. This is a recipe for truncation/conversion errors. Worse, passing-1
will silently behave as iflen==255
was passed.You used
BOOST_ASSERT
which is not part of the Boost Test library, and compiles away in release build (whenNDEBUG
is defined). You probably wantedBOOST_CHECK
or, indeed,BOOST_CHECK_EQUAL
here. If you compile with warnings enabled you will actually be informed of mistakes like these (becauseobs
andexp
were never used in release mode).you were missing
BOOST_AUTO_TEST_SUITE_END()
you should probably supply link-dependencies instead of manually specifying boost include-directories in cmake:
target_link_libraries(test_binary_search Boost::unit_test_framework)
Since you know you don't need runtime link dependencies, you can use the
headers
target to just get the includes:target_link_libraries(test_binary_search Boost::headers)
also, using target-specific flags/libraries etc. obviates the need for
include_directories
on my Boost version (a develop version after 1.88) there are deprecated header warnings, so I'll define BOOST_ALLOW_DEPRECATED_HEADERS
Given all these, here's my counter-proposition:
File
src/binary_search.cpp
#include "binary_search.hpp" // implementations of non-template functions here
File
src/binary_search.hpp
#pragma once #include <cstdint> namespace mylib { template <typename T> T my_binary_search(T A[], uint8_t len, T nu) { uint8_t low = 0; uint8_t high = len - 1; while (low <= high) { uint8_t mid = low + (high - low) / 2; if (nu == A[mid]) return mid; else if (nu > A[mid]) low = mid + 1; else high = mid - 1; } return -1; } } // namespace mylib #include <span> namespace mylib { template <typename T, std::size_t N, typename V> // T sehe_binary_search(std::span<T, N> a, V const& nu) { uint8_t low = 0; uint8_t high = a.size() - 1; while (low <= high) { uint8_t mid = low + (high - low) / 2; if (nu == a[mid]) return mid; else if (nu > a[mid]) low = mid + 1; else high = mid - 1; } return -1; } } // namespace mylib
File
test/test_binary_search.cpp
#define BOOST_TEST_MODULE test_algorithms #include "../src/binary_search.hpp" #include <boost/test/data/test_case.hpp> #include <boost/test/included/unit_test.hpp> #include <boost/test/included/unit_test_framework.hpp> #include <boost/test/tools/output_test_stream.hpp> #include <cstdint> namespace utf = boost::unit_test; using Arr8 = std::array<int32_t, 8>; // make streamable for boost test: namespace std { std::ostream& boost_test_print_type(std::ostream& os, Arr8 const& arr) { os << "{"; for (char const* sep = ""; auto const& el : arr) os << std::exchange(sep, ", ") << el; return os << "}"; } } // namespace std BOOST_AUTO_TEST_SUITE(test_search) namespace { // file static visibility std::array<Arr8, 4> constexpr input_arrs{{ {26, 31, 41, 41, 58, 59, 101, 104}, {26, 31, 41, 41, 58, 59, 101, 104}, {1, 4, 5, 7, 19, 28, 45, 92}, {1, 4, 5, 7, 19, 28, 45, 92}, }}; int32_t constexpr input_vals[] = {31, 32, 92, 101}; int32_t constexpr expected_vals[] = {1, -1, 7, -1}; auto test_cases = // utf::data::make(input_arrs) ^ // utf::data::make(input_vals) ^ // utf::data::make(expected_vals); } // namespace BOOST_DATA_TEST_CASE(my_binary_search, test_cases, input_arr, nu, exp) { int32_t tmp[8]; std::copy(std::begin(input_arr), std::end(input_arr), std::begin(tmp)); auto obs = mylib::my_binary_search(tmp, 8, nu); BOOST_CHECK_EQUAL(obs, exp); } BOOST_DATA_TEST_CASE(sehe_binary_search, test_cases, input_arr, nu, exp) { auto obs = mylib::sehe_binary_search(std::span(input_arr), nu); BOOST_CHECK_EQUAL(obs, exp); } BOOST_AUTO_TEST_SUITE_END()
File
test/CMakeLists.txt
cmake_minimum_required(VERSION 3.16) set(CMAKE_EXPORT_COMPILE_COMMANDS On) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++20 -pedantic -Wall -Wextra -Werror") project(test_algorithms) set(SOURCES ../src/binary_search.cpp test_binary_search.cpp ) find_package(Boost 1.87.0 REQUIRED COMPONENTS unit_test_framework) if (Boost_FOUND) enable_testing() add_executable(test_binary_search ${SOURCES}) target_link_libraries(test_binary_search Boost::headers) target_compile_definitions(test_binary_search PRIVATE BOOST_ALLOW_DEPRECATED_HEADERS) add_test(NAME test_binary_search COMMAND test_binary_search) endif()
With a live demo:
本文标签: cUnable to Compile Parameterized Boost TestsStack Overflow
版权声明:本文标题:c++ - Unable to Compile Parameterized Boost Tests - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744834760a2627564.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
use of undeclared identifier 'binary_search'
because definition of the template functionbinary_search
is inaccessible when you compiletest_binary_search.cpp
. With separate test file you cannot test things which are defined only in a source file. You may test only functionality declared in a header file (which you could include into the test file.) – Tsyvarev Commented Mar 10 at 22:04T(&)[N]
not being a copyable value-type in the first place. See my answer for some other gotchas hidden in the question :) – sehe Commented Mar 10 at 22:46binary_search.cpp
file instead of correctbinary_search.hpp
is a "copy/paste problem", then why you have described it in your answer instead of fixing the question post itself? – Tsyvarev Commented Mar 11 at 0:11