I just installed C++ Builder 12 and wanted to compile an ancient piece of software that uses a TFileStream to read and write to c style buffers. For instance:
DWORD MyFileStream::Read (BYTE *Buffer, DWORD Count)
{
try
{
if (File)
return File->Read(Buffer, Count) ;
}
catch(...)
{
LastError = GetLastError() ;
return MY_ERROR_READ ;
}
return 0x00 ;
}
This still compiles ok in 32 bit, but (going from c++ Builder 11.3 to 12) it now doesn't compile anymore in 64 bit. So, after 20 years or so I had to go and have a look at TFileStream in the help file and it appears to solely use a DynamicArray ?
Confused ? Why does it compile in 32 bit then ? I have a feeling the help file is not complete ?? And more importantly, how do I fix this without creating a dynamic array (assuming that is still possible) ?
The error:
call to member function 'Read' is ambiguous
[C++ Error] System.Classes.hpp(1756, 36): candidate function
[C++ Error] System.Classes.hpp(1771, 38): candidate function
[C++ Error] System.Classes.hpp(1777, 36): candidate function not viable: no known conversion from 'BYTE *' (aka 'unsigned char *') to 'System::Sysutils::TBytes &' (aka 'DynamicArray<unsigned char> &') for 1st argument
[C++ Error] System.Classes.hpp(1779, 38): candidate function not viable: no known conversion from 'BYTE *' (aka 'unsigned char *') to 'System::Sysutils::TBytes &' (aka 'DynamicArray<unsigned char> &') for 1st argument
[C++ Error] System.Classes.hpp(1773, 36): candidate function not viable: requires 3 arguments, but 2 were provided
[C++ Error] System.Classes.hpp(1775, 38): candidate function not viable: requires 3 arguments, but 2 were provided
So, after 20 years or so I had to go and have a look at TFileStream in the help file and it appears to solely use a DynamicArray ?
That is incorrect. It still has overloads for working with raw buffers.
The real culprit is that Read()
(and Write()
) has additional overloads under 64-bit only, which are not documented yet but you would see them if you look at the declarations in System.Classes.hpp
:
public:
virtual System::LongInt __fastcall Read(void *Buffer, System::LongInt Count)/* overload */;
virtual System::LongInt __fastcall Write(const void *Buffer, System::LongInt Count)/* overload */;
#ifdef _WIN64
virtual System::NativeInt __fastcall Read(void *Buffer, System::NativeInt Count)/* overload */;
virtual System::NativeInt __fastcall Write(const void *Buffer, System::NativeInt Count)/* overload */;
#endif /* _WIN64 */
virtual System::LongInt __fastcall Read(System::Sysutils::TBytes Buffer, System::LongInt Offset, System::LongInt Count)/* overload */;
virtual System::LongInt __fastcall Write(const System::Sysutils::TBytes Buffer, System::LongInt Offset, System::LongInt Count)/* overload */;
#ifdef _WIN64
virtual System::NativeInt __fastcall Read(System::Sysutils::TBytes Buffer, System::NativeInt Offset, System::NativeInt Count)/* overload */;
virtual System::NativeInt __fastcall Write(const System::Sysutils::TBytes Buffer, System::NativeInt Offset, System::NativeInt Count)/* overload */;
#endif /* _WIN64 */
System::LongInt __fastcall Read(System::Sysutils::TBytes &Buffer, System::LongInt Count)/* overload */;
System::LongInt __fastcall Write(const System::Sysutils::TBytes Buffer, System::LongInt Count)/* overload */;
#ifdef _WIN64
System::NativeInt __fastcall Read(System::Sysutils::TBytes &Buffer, System::NativeInt Count)/* overload */;
System::NativeInt __fastcall Write(const System::Sysutils::TBytes Buffer, System::NativeInt Count)/* overload */;
#endif /* _WIN64 */
This means that under 64-bit only, the 2-param Read()
which you are trying to call now accepts both LongInt
and NativeInt
types for the buffer size, and since DWORD
is implicitly convertible to both types, that is why your code is now ambiguous under 64-bit but not under 32-bit.
To solve this, you would need to explicitly tell the compiler which overload you want to call. You can either:
DWORD
to the desired integer type, eg:return File->Read(Buffer, static_cast<LongInt>(Count));
Read
method itself to the desired overload type before calling it, eg:typedef LongInt __fastcall (__closure *TReadMethod)(void*, LongInt);
static_cast<TReadMethod>(stream->Read)(buffer, size);