"Incomplete Type" when open hashing c++ - c++11

Im new to C++ and I'm getting an error which I've never seen before. I am attempting to search by a backtracking algorithm that uses a hash table (open hashing).
Before I make a recursive call, I'm searching the hash table to see if the current position has been searched before (and has failed) and then inserting vector of ints "B" into the hash table when the recursive call returns false so that future search for the same position can be avoided.
Heres my function in my program calling file to hash:
bool open_hash_solve (vector<int> B, vector<Move> & MS, vector<Move> & TMS, OpenHashTable<vector<int>> & H) {
if (solved(B))
return true;
vector<Move> curr = currentMoves(B, TMS);
for (int m = 0; m < curr.size(); m++) {
vector<int> moveMade = makeMove(B, curr[m]);
if (!H.contains(moveMade)){
if (open_hash_solve(moveMade, MS, TMS, H)){
MS.insert(MS.begin(), curr[m]);
return true;
}
else
H.insert(moveMade);
}
}
return false;
}
Header file Separate Chaining from the textbook:
#ifndef SEPARATE_CHAINING_H
#define SEPARATE_CHAINING_H
#include <vector>
#include <list>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
int nextPrime( int n );
// SeparateChaining Hash table class
//
// CONSTRUCTION: an approximate initial size or default of 101
//
// ******************PUBLIC OPERATIONS*********************
// bool insert( x ) --> Insert x
// bool remove( x ) --> Remove x
// bool contains( x ) --> Return true if x is present
// void makeEmpty( ) --> Remove all items
template <typename HashedObj>
class OpenHashTable
{
public:
explicit OpenHashTable( int size = 101 ) : currentSize{ 0 }
{ theLists.resize( 101 ); }
bool contains( const HashedObj & x ) const
{
auto & whichList = theLists[ myhash( x ) ];
return find( begin( whichList ), end( whichList ), x ) != end( whichList );
}
void makeEmpty( )
{
for( auto & thisList : theLists )
thisList.clear( );
}
bool insert( const HashedObj & x )
{
auto & whichList = theLists[ myhash( x ) ];
if( find( begin( whichList ), end( whichList ), x ) != end( whichList) )
return false;
whichList.push_back( x );
// Rehash; see Section 5.5
if( ++currentSize > theLists.size( ) )
rehash( );
return true;
}
bool insert( HashedObj && x )
{
auto & whichList = theLists[ myhash( x ) ];
if( find( begin( whichList ), end( whichList ), x ) != end( whichList ) )
return false;
whichList.push_back( std::move( x ) );
// Rehash; see Section 5.5
if( ++currentSize > theLists.size( ) )
rehash( );
return true;
}
bool remove( const HashedObj & x )
{
auto & whichList = theLists[ myhash( x ) ];
auto itr = find( begin( whichList ), end( whichList ), x );
if( itr == end( whichList ) )
return false;
whichList.erase( itr );
--currentSize;
return true;
}
private:
vector<list<HashedObj>> theLists; // The array of Lists
int currentSize;
void rehash( )
{
vector<list<HashedObj>> oldLists = theLists;
// Create new double-sized, empty table
theLists.resize( nextPrime( 2 * theLists.size( ) ) );
for( auto & thisList : theLists )
thisList.clear( );
theLists.resize( nextPrime( 2 * theLists.size( ) ) );
for( auto & thisList : theLists )
thisList.clear( );
// Copy table over
currentSize = 0;
for( auto & thisList : oldLists )
for( auto & x : thisList )
insert( std::move( x ) );
}
size_t myhash( const HashedObj & x ) const
{
static hash<HashedObj> hf; ***** ERROR HERE *****
return hf( x ) % theLists.size( );
}
};
#endif
My Error:
SeparateChaining.h: In instantiation of ‘size_t OpenHashTable<HashedObj>::myhash(const HashedObj&) const [with HashedObj = std::vector<int>; size_t = long unsigned int]’:
SeparateChaining.h:33:44: required from ‘bool OpenHashTable<HashedObj>::contains(const HashedObj&) const [with HashedObj = std::vector<int>]’
movetest1.cpp:107:29: required from here
SeparateChaining.h:106:34: error: ‘std::hash<std::vector<int> > hf’ has incomplete type
static hash<HashedObj> hf;
^
How do I fix this??
Let me know if my description doesn't make sense or needs clarity. Thanks in advance!

The reason is that std::hash is not defined for std::vector except std::vector<bool>. It's defined only for basic types and select library types. You have to implement your own hashing algorithm.
Regarding your last comment, hash( x ) is invalid code since std::hash is a class template, not a function template.
You also can't write your own specialization for std::hash<std::vector<int>> since std::vector<int> is not a user-defined type.
Appropriate solution would be in the style of C++ standard library - OpenHashTable should allow its user to specify hashing algorithm:
template<typename HashedObj, typename Hash = std::hash<HashedObj>>
class OpenHashTable {
// ...
size_t myhash(const HashedObj& x) const {
static Hash hf;
return hf(x) % theLists.size();
}
};
So you can use it as follows:
struct my_vector_int_hash {
size_t operator()(const std::vector<int>& v) const {
// your code here
}
};
OpenHashTable<std::vector<int>, my_vector_int_hash> hashtable;
Update: as long as discussion arose about whether you can specialize std::hash<std::vector<int>>, here is why you are not allowed to do it.
[namespace.std]/1 says:
The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
std::vector<int> is not a user-defined type, nor it depends on any user-defined type, so such specialization is illegal and leads to undefined behavior.

Related

Having an access exception with a mapped file being deleted

I want to simulate that an access to a file mapped into memory results in an access violation when the file is being delete while it is accessed through a mapping. That's my current code:
#include <Windows.h>
#include <iostream>
#include <atomic>
using namespace std;
using XHANDLE = unique_ptr<void, decltype([]( void *h ) { h && h != INVALID_HANDLE_VALUE && CloseHandle( (HANDLE)h ); })>;
using XMAP_VIEW = unique_ptr<void, decltype([]( void *p ) { p && UnmapViewOfFile( p ); })>;
template<typename Fn, typename Filter, typename Handler>
requires requires( Fn fn, Filter filter, EXCEPTION_POINTERS *pEp, Handler handler ) { { fn() }; { filter( pEp ) } -> same_as<LONG>; { handler() }; }
void seh_encapsulate( Fn fn, Filter filter, Handler handler );
int wmain( int argc, wchar_t **argv )
{
if( argc < 2 )
return EXIT_FAILURE;
XHANDLE xhFile( CreateFileW( argv[1], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) );
if( xhFile.get() == INVALID_HANDLE_VALUE )
return EXIT_FAILURE;
LARGE_INTEGER liFileSize;
if( !GetFileSizeEx( xhFile.get(), &liFileSize ) || liFileSize.QuadPart > (size_t)-1 )
return EXIT_FAILURE;
XHANDLE xhMappging( CreateFileMapping( xhFile.get(), nullptr, PAGE_READONLY, 0, 0, nullptr ) );
if( !xhMappging.get() )
return EXIT_FAILURE;
XMAP_VIEW mapView( MapViewOfFile( xhMappging.get(), FILE_MAP_READ, 0, 0, 0 ) );
if( !mapView.get() )
return EXIT_FAILURE;
atomic_char
*pa = (atomic_char *)mapView.get(),
*paEnd = pa + (size_t)liFileSize.QuadPart;
seh_encapsulate(
[&]()
{
for( ; ; )
for( atomic_char *paScn = pa; paScn != paEnd; ++paScn )
(void)paScn->load( memory_order_relaxed );
},
[&]( EXCEPTION_POINTERS *pEp ) -> LONG
{
if( pEp->ExceptionRecord->ExceptionCode != EXCEPTION_IN_PAGE_ERROR )
return EXCEPTION_CONTINUE_SEARCH;
if( pEp->ExceptionRecord->NumberParameters < 2 )
return EXCEPTION_CONTINUE_SEARCH;
void *where = (void *)pEp->ExceptionRecord->ExceptionInformation[1];
if( where < pa || where >= paEnd )
return EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_EXECUTE_HANDLER;
},
[]()
{
cout << "I/O error" << endl;
} );
}
template<typename Fn, typename Filter, typename Handler>
requires requires( Fn fn, Filter filter, EXCEPTION_POINTERS *pEp, Handler handler ) { { fn() }; { filter( pEp ) } -> same_as<LONG>; { handler() }; }
void seh_encapsulate( Fn fn, Filter filter, Handler handler )
{
__try
{
fn();
}
__except( filter( GetExceptionInformation() ) )
{
handler();
}
}
"Unfortunately" I can delete the file but the clusters which occupied the file on the disk are retained until the mapping is closed.
Do you have any idea how I could make the clusters being unmapped so that my experiment will work ?
Aside from that the above code nicely shows how to have Structured Exception Handling "in" a function with stack-unwinding.
My intent was simply to find out if I/O errors occuring with a memory mapped file could be catched. My final idea was to copy a large file to an USB stick, unplug und plug it so that the cached data with that stick is flushed, then run the above program with that and unplug the stick while the code is running. And the result was like I expected: the I/O error results in an access violation which is caught by the above SEH handler.

How can I have stack-unwinding while using Structrured Exception Handling

How can I have stack-unwinding while using Structrured Exception Handling ?
I'm gonna ask my question after that because I've found a way to do that at least at a syntactical level and I thought this might be useful for others.
I found a way to have stack unwinding while using Structured Exception Handling.
The following code catches an I/O error while using a memory-mapped file:
#include <Windows.h>
#include <iostream>
#include <atomic>
using namespace std;
using XHANDLE = unique_ptr<void, decltype([]( void *h ) { h && h != INVALID_HANDLE_VALUE && CloseHandle( (HANDLE)h ); })>;
using XMAP_VIEW = unique_ptr<void, decltype([]( void *p ) { p && UnmapViewOfFile( p ); })>;
template<typename Fn, typename Filter, typename Handler>
requires requires( Fn fn, Filter filter, EXCEPTION_POINTERS *pEp, Handler handler ) { { fn() }; { filter( pEp ) } -> same_as<LONG>; { handler() }; }
void seh_encapsulate( Fn fn, Filter filter, Handler handler );
int wmain( int argc, wchar_t **argv )
{
if( argc < 2 )
return EXIT_FAILURE;
XHANDLE xhFile( CreateFileW( argv[1], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) );
if( xhFile.get() == INVALID_HANDLE_VALUE )
return EXIT_FAILURE;
LARGE_INTEGER liFileSize;
if( !GetFileSizeEx( xhFile.get(), &liFileSize ) || liFileSize.QuadPart > (size_t)-1 )
return EXIT_FAILURE;
XHANDLE xhMappging( CreateFileMapping( xhFile.get(), nullptr, PAGE_READONLY, 0, 0, nullptr ) );
if( !xhMappging.get() )
return EXIT_FAILURE;
XMAP_VIEW mapView( MapViewOfFile( xhMappging.get(), FILE_MAP_READ, 0, 0, 0 ) );
if( !mapView.get() )
return EXIT_FAILURE;
atomic_char
*pa = (atomic_char *)mapView.get(),
*paEnd = pa + (size_t)liFileSize.QuadPart;
seh_encapsulate(
[&]()
{
for( ; ; )
for( atomic_char *paScn = pa; paScn != paEnd; ++paScn )
(void)paScn->load( memory_order_relaxed );
},
[&]( EXCEPTION_POINTERS *pEp ) -> LONG
{
if( pEp->ExceptionRecord->ExceptionCode != EXCEPTION_IN_PAGE_ERROR )
return EXCEPTION_CONTINUE_SEARCH;
if( pEp->ExceptionRecord->NumberParameters < 2 )
return EXCEPTION_CONTINUE_SEARCH;
void *where = (void *)pEp->ExceptionRecord->ExceptionInformation[1];
if( where < pa || where >= paEnd )
return EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_EXECUTE_HANDLER;
},
[]()
{
cout << "I/O error" << endl;
} );
}
template<typename Fn, typename Filter, typename Handler>
requires requires( Fn fn, Filter filter, EXCEPTION_POINTERS *pEp, Handler handler ) { { fn() }; { filter( pEp ) } -> same_as<LONG>; { handler() }; }
void seh_encapsulate( Fn fn, Filter filter, Handler handler )
{
__try
{
fn();
}
__except( filter( GetExceptionInformation() ) )
{
handler();
}
}
The trick with that is to have the code with the SEH-exception to be called, the filter- and the handler-function to be function objects of seh_encapsulate. seh_encapsulate normally won't be inlined since for the compiler it needs to be code with a separate function body since it uses Structured Exception Handling. The code of the "Function" object mustn't use any stack unwinding since the thrown exception from inside that might bypass any object destruction. But the "Filter" object as well as the "Handler" object can have stack unwinding. And the surrounding code of seh_encapsulate can have stack unwinding as well.
So at least at a syntatical level it looks like you have Structured Exception Handling in a function that has object unwinding.
IInspectable pointed out that operator << could throw an exception. I argued that this won't happen and if this would happen for other reasons the lambda would be compiled into a separate function being called from my seh_encapsulate.
I just checked that with the following example code:
#include <Windows.h>
#include <iostream>
#include <string>
using namespace std;
int main()
{
__try
{
char const *outer = "world";
[&]()
{
string inner( "hello" );
inner += " ";
inner += outer;
cout << inner << endl;
}();
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
}
}
If you run the code as a release-compile in the debugger you can see that the lambda gets called separately:
lea rax,[outer]
mov qword ptr [rsp+20h],rax
lea rcx,[rsp+20h]
call `main'::`3'::<lambda_1>::operator()
The above code inside the lambda might throw bad_alloc and has object unwinding as well.
So this discussion leaded to a sometimes simpler solution like that in my first answer and clarified some non-relevant issues.

passing a parameter pack over a legacy function signature using forward_as_tuple

In my app I would like to pass in a parameter pack over a legacy function signature, and change the values. Here is code that illustrates my question with my attempts as comments:
#include <tuple>
#include <cassert>
void LegacySignature( void* param );
template< typename... ArgsT >
// using ???; // attempt: can 'template alias' or 'using declaration' make the pack's type visible so I can use it inside the LegacyFunction?
void MyFunc( ArgsT&... args )
{
auto userArgsTuple = std::forward_as_tuple< ArgsT&... >( args... );
LegacySignature( &userArgsTuple );
}
void LegacySignature( void* param )
{
// auto userArgsTuple = reinterpret_cast<???>( param ); // attempt: how can I get the parameter pack's type declared so I can use it here?
// do something with the params like change num to 44 and tf to true;
//userArgsTuple->num = 44; // desired functionality
//userArgsTuple->tf = true; // desired functionality
}
int main()
{
int num { 33 };
bool tf { false };
MyFunc( num, tf );
assert( num == 44 && tf == true );
return 0;
}
Is there a way to make the parameter pack a declarable lvalue?
I'm assuming what you want is a function pointer to your legacy signature.
Here is a C++11 approach.
template<class Sig, class F>
struct magic_callback_t;
template<class R, class...Args, class F>
struct magic_callback_t<R(Args...), F> {
F f;
void* pvoid() const { return this; }
using result_sig = R(*)(void*, Args...);
result_sig pfunc() const {
return [](void* pvoid, Args...args)->R{
auto* self = static_cast<magic_callback_t*>(pvoid);
return (self->f)(std::forward<Args>(args)...);
};
}
};
template<class Sig, class F>
magic_callback_t<Sig, F> magic_callback( F&& f ) {
return {std::forward<F>(f)};
}
Now we just do this:
auto callback = magic_callback( [&](){
// use whatever as if we where in the enclosing scope
});
void(*)(void*) legacy_ptr = callback.pfunc();
legacy_ptr( callback.pvoid() );
will call the lambda you passed to magic_callback.
If you want to store stuff as a tuple, you can. Just capture the tuple in the lambda, then use std::get to access it in the body of the lambda. Use mutable if you want it to be mutable.
The code below fixes the sample code so that it answers the question as to how to pass a parameter pack over a legacy function signature using forward_as_tuple.
#include <tuple>
#include <cassert>
#include <memory>
#include <functional>
#define ARGSET int, bool
void LegacySignature( long* param ); // ie, LPARAM
template< typename... ArgsT >
struct MyParams
{
MyParams( ArgsT... args ) : rvalRefs { std::forward_as_tuple( args... ) } {} // The resulting forward_as_tuple tuple has rvalue reference data members
std::tuple< ArgsT... > rvalRefs;
};
void LegacySignature( long* legSigParam )
{
auto userArgsTuple( reinterpret_cast< MyParams< ARGSET >* >( legSigParam ) );
// do something with the params like change num to 44 and tf to true;
std::get< 0 >( userArgsTuple->rvalRefs ) = 44; // index types can probably be worked out using enums
std::get< 1 >( userArgsTuple->rvalRefs ) = true;
}
int main()
{
int num { 33 };
bool tf { false };
MyParams< ARGSET > myParams( num, tf );
std::unique_ptr< MyParams< ARGSET > > legSigParamPtr = std::make_unique< MyParams< ARGSET > >( myParams );
LegacySignature( ( long* )legSigParamPtr.get() );
assert( std::get< 0 >( legSigParamPtr->rvalRefs ) == 44 && std::get< 1 >( legSigParamPtr->rvalRefs ) == true );
return 0;
}

How to return nullptr from a lambda function?

I have a small lambda function which shall find and return a QTreeWidgetItem. But if it does not find the given item, then it shall return a nullptr. But if I try to compile it then it gives me an error.
The function:
auto takeTopLevelItem = []( QTreeWidget* aTreeWidget, const QString& aText )
{
const int count = aTreeWidget->topLevelItemCount();
for ( int index = 0; index < count; ++index )
{
auto item = aTreeWidget->topLevelItem( index );
if ( item->text( 0 ) == aText )
{
return aTreeWidget->takeTopLevelItem( index );
}
}
return nullptr; // This causes a compilation error.
};
The error:
Error 1 error C3487: 'nullptr': all return expressions in a lambda must have the same type: previously it was 'QTreeWidgetItem *' cpp 251
I changed the mentioned line with this and now it compiles:
return (QTreeWidgetItem*)( nullptr );
but I would like to avoid this syntax. How can I solve this ?
I use Visual Studio 2012.
You can add an explicit return type annotation:
auto takeTopLevelItem = []( ... ) -> QTreeWidgetItem*
{
// ...
}
That way nullptr will be converted to your pointer type properly. You're getting that error because the lambda assumes no conversions should be made, and treats nullptr_t as a legitimate alternative return type.
As a side note, consider using (std::)optional instead. The nullability of pointers can be used to represent a missing return, but it doesn't mean it necessarily should be.
If you just want to avoid the syntax, rather than the casting, you could it like this:
static_cast<QTreeWidgetItem*>(nullptr);
I made a small example, on how Bartek's and mine's answer really work:
#include <iostream>
class A {
int a;
};
auto bla = [] (A* obj, bool flag) -> A* {
if(flag)
return obj;
return nullptr;
// return static_cast<A*>(nullptr);
};
int main() {
A obj;
A* ptr = &obj;
bool flag = false;
if( bla(ptr, flag) == nullptr)
std::cout << "ok\n";
return 0;
}
I had this very same issue with some Smart Pointers, so I found I could do this to avoid the issue:
auto myFunc= [](MyClass* class)
{
MyPointer* pointer = nullptr;
if( class && class->isValid() )
{
pointer = class->getPointerInstance()
}
return pointer;
}
Similarly, for shared pointer, just repleace MyPointer* by std::shared_ptr<MyPointer>.
So your code would looks like:
auto takeTopLevelItem = []( QTreeWidget* aTreeWidget, const QString& aText )
{
QTreeWidgetItem* item = nullptr;
const int count = aTreeWidget->topLevelItemCount();
for ( int index = 0; index < count; ++index )
{
auto item = aTreeWidget->topLevelItem( index );
if ( item->text( 0 ) == aText )
{
item = aTreeWidget->takeTopLevelItem( index );
break;
}
}
return item;
};

Find out if UNC path is pointing to local machine

Is there any simple way to tell if UNC path points to a local machine.
I found the following question SO
Is there any WIN32 API that will do the same?
#include <windows.h>
#include <WinSock.h>
#include <string>
#include <algorithm>
#pragma comment(lib, "wsock32.lib")
using namespace std;
std::wstring ExtractHostName( const std::wstring &share )
{
if (share.size() < 3 )
return L"";
size_t pos = share.find( L"\\", 2 );
wstring server = ( pos != string::npos ) ? share.substr( 2, pos - 2 ) : share.substr( 2 );
transform( server.begin(),server.end(), server.begin(), tolower);
return server;
}
bool IsIP( const std::wstring &server )
{
size_t invalid = server.find_first_not_of( L"0123456789." );
bool fIsIP = ( invalid == string::npos );
return fIsIP;
}
bool IsLocalIP( const std::wstring &ipToCheck )
{
if ( ipToCheck == L"127.0.0.1" )
return true;
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 1, 1 );
if ( WSAStartup( wVersionRequested, &wsaData ) != 0 )
return false;
bool fIsLocal = false;
char hostName[255];
if( gethostname ( hostName, sizeof(hostName)) == 0 )
{
PHOSTENT hostinfo;
if(( hostinfo = gethostbyname(hostName)) != NULL )
{
for (int i = 0; hostinfo->h_addr_list[i]; i++)
{
char *ip = inet_ntoa(*( struct in_addr *)hostinfo->h_addr_list[i]);
wchar_t wcIP[100]={0};
::MultiByteToWideChar(CP_ACP, 0, ip, -1, wcIP, _countof(wcIP));
if (ipToCheck == wcIP)
{
fIsLocal = true;
break;
}
}
}
}
WSACleanup();
return fIsLocal;
}
bool IsLocalHost( const std::wstring &server )
{
if (server == L"localhost")
return true;
bool fIsLocalHost = false;
wchar_t buffer[MAX_PATH]={0};
DWORD dwSize = _countof(buffer);
BOOL fRet = GetComputerName( buffer, &dwSize );
transform( buffer, buffer + dwSize, buffer, tolower);
fIsLocalHost = ( server == buffer );
return fIsLocalHost;
}
bool ShareIsLocal( const std::wstring &share )
{
wstring server = ExtractHostName( share );
bool fIsIp = IsIP( server );
if ( fIsIp )
return IsLocalIP( server );
else
return IsLocalHost( server );
}

Resources