Skip to content

Commit 4ac621e

Browse files
committed
Fix extractTo overload ambiguity for single item indices
When calling `BitInputArchive::extractTo` with a single `std::uint32_t` index, particularly the literal `0`, overload resolution could be ambiguous. This occurred because `0` is convertible to both `BitIndicesView` and `std::function`-based callbacks. Adding explicit `std::uint32_t` overloads ensures correct resolution through standard conversions, improving usability.
1 parent d132b2f commit 4ac621e

3 files changed

Lines changed: 86 additions & 3 deletions

File tree

include/bit7z/bitinputarchive.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ class BitInputArchive {
255255
*/
256256
void extractTo( const tstring& outDir, BitIndicesView indices = {} ) const;
257257

258+
/**
259+
* @brief Extracts the item at the given index to the chosen directory.
260+
*
261+
* @param outDir the output directory where the extracted file will be put.
262+
* @param index the index of the item to be extracted.
263+
*/
264+
void extractTo( const tstring& outDir, std::uint32_t index ) const;
265+
258266
/**
259267
* @brief Extracts to the output directory all the items whose paths match the given wildcard pattern.
260268
*
@@ -508,6 +516,14 @@ class BitInputArchive {
508516
*/
509517
void extractTo( ItemBufferCallback callback, BitIndicesView indices = {} ) const;
510518

519+
/**
520+
* @brief Extracts the item at the given index to the buffer provided by the given ItemBufferCallback.
521+
*
522+
* @param callback the function providing the buffer.
523+
* @param index the index of the item to be extracted.
524+
*/
525+
void extractTo( ItemBufferCallback callback, std::uint32_t index ) const;
526+
511527
/**
512528
* @brief Extracts to the buffers provided by the given ItemBufferCallback
513529
* all the items that satisfy the given filtering criteria.

src/bitinputarchive.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,16 @@ void BitInputArchive::extractMatchingTo( const tstring& outDir, const tregex& re
562562

563563
#endif
564564

565+
/* Note: even though BitIndicesView is implicitly constructible from a single index, this overload is needed
566+
* because plain zero indices (e.g., a literal 0) are also null pointer constants: without it, they would be
567+
* ambiguously convertible to both BitIndicesView and the std::function-based callbacks of the sibling
568+
* overloads. A std::uint32_t argument instead binds here via a standard conversion,
569+
* which takes precedence over the user-defined ones. */
570+
void BitInputArchive::extractTo( const tstring& outDir, std::uint32_t index ) const {
571+
// Note: the explicit BitIndicesView is needed to avoid recursing into this same overload.
572+
extractTo( outDir, BitIndicesView{ index } );
573+
}
574+
565575
void BitInputArchive::extractTo( const tstring& outDir, FilterCallback filterCallback ) const {
566576
const auto callback = bit7z::make_com< FileExtractCallback, ExtractCallback >(
567577
*this,
@@ -914,6 +924,16 @@ void BitInputArchive::extractTo( ItemBufferCallback callback, BitIndicesView ind
914924
extractArchive( extractCallback, ExtractMode::Extract, indices );
915925
}
916926

927+
/* Note: even though BitIndicesView is implicitly constructible from a single index, this overload is needed
928+
* because plain zero indices (e.g., a literal 0) are also null pointer constants: without it, they would be
929+
* ambiguously convertible to both BitIndicesView and the FilterCallback of the sibling overload.
930+
* A std::uint32_t argument instead binds here via a standard conversion,
931+
* which takes precedence over the user-defined ones. */
932+
void BitInputArchive::extractTo( ItemBufferCallback callback, std::uint32_t index ) const {
933+
// Note: the explicit BitIndicesView is needed to avoid recursing into this same overload.
934+
extractTo( std::move( callback ), BitIndicesView{ index } );
935+
}
936+
917937
void BitInputArchive::extractTo( ItemBufferCallback callback, FilterCallback filterCallback ) const {
918938
if ( !callback ) {
919939
throw BitException(

tests/src/test_bitinputarchive.cpp

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,12 +2048,59 @@ TEMPLATE_TEST_CASE(
20482048
}
20492049

20502050
SECTION( "A valid index extracts correctly" ) {
2051-
/* Note: passing a plain 0 here would be ambiguous between the BitIndicesView
2052-
* and FilterCallback overloads, so we need to explicitly use a BitIndicesView. */
2051+
REQUIRE_NOTHROW( info.extractTo( bufferCallback, 0 ) );
2052+
REQUIRE( crc32( outBuffer ) == clouds.crc32 );
2053+
}
2054+
}
2055+
2056+
// NOLINTNEXTLINE(*-err58-cpp)
2057+
TEST_CASE( "BitInputArchive: Single-index extractTo overloads resolve unambiguously for plain index values",
2058+
"[bitinputarchive]" ) {
2059+
const TestDirectory testDir{ fs::path{ test_archives_dir } / "extraction" / "single_file" };
2060+
2061+
const auto arcFileName = fs::path{ clouds.name }.concat( ".7z" );
2062+
2063+
tstring inputArchive;
2064+
getInputArchive( arcFileName, inputArchive );
2065+
const BitArchiveReader info( test::sevenzipLib(), inputArchive, BitFormat::SevenZip );
2066+
2067+
buffer_t outBuffer;
2068+
const auto bufferCallback = [ &outBuffer ]( const BitArchiveItem&, const tstring& ) -> buffer_t& {
2069+
return outBuffer;
2070+
};
2071+
2072+
SECTION( "Extracting via ItemBufferCallback using a plain index" ) {
2073+
/* Regression check: previously, a plain 0 was ambiguous between
2074+
* the BitIndicesView and FilterCallback overloads. */
2075+
REQUIRE_NOTHROW( info.extractTo( bufferCallback, 0 ) );
2076+
REQUIRE( crc32( outBuffer ) == clouds.crc32 );
2077+
2078+
/* Note: MSVC treats const-zero variables as null pointer constants,
2079+
* so this also used to be ambiguous. */
20532080
const std::uint32_t index = 0;
2054-
REQUIRE_NOTHROW( info.extractTo( bufferCallback, BitIndicesView( index ) ) );
2081+
REQUIRE_NOTHROW( info.extractTo( bufferCallback, index ) );
20552082
REQUIRE( crc32( outBuffer ) == clouds.crc32 );
20562083
}
2084+
2085+
SECTION( "Extracting to a directory using a plain index" ) {
2086+
/* Regression check: previously, a plain 0 was ambiguous between the BitIndicesView,
2087+
* FilterCallback, RenameCallback, and LegacyRenameCallback overloads. */
2088+
const TempTestDirectory outputDir{ "test_bitinputarchive" };
2089+
INFO( "Test directory: " << outputDir )
2090+
2091+
REQUIRE_NOTHROW( info.extractTo( outputDir, 0 ) );
2092+
for ( const auto& expectedItem : singleFileContent().items ) {
2093+
REQUIRE_FILESYSTEM_ITEM( expectedItem );
2094+
}
2095+
REQUIRE( fs::is_empty( outputDir.path() ) );
2096+
}
2097+
2098+
SECTION( "An out-of-range plain index must be rejected with BitError::InvalidIndex" ) {
2099+
REQUIRE_THROWS_CODE( info.extractTo( bufferCallback, 999 ), BitError::InvalidIndex );
2100+
2101+
const TempTestDirectory outputDir{ "test_bitinputarchive" };
2102+
REQUIRE_THROWS_CODE( info.extractTo( outputDir, 999 ), BitError::InvalidIndex );
2103+
}
20572104
}
20582105

20592106
// NOLINTNEXTLINE(*-err58-cpp)

0 commit comments

Comments
 (0)