Thread: A Challenge in C++

  1. #16
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    The problem of having many threads doing very little is that the overhead of managing the threads (be it in the OS or your own custom scheduler) may outweigh the actual work accomplished by the threads.

    On the other hand, if you have a massively parallel system like the one you describe, having many threads would be a good thing. Ideally, a program should have about as many threads as available cores if it's computation-bound. I/O-bound processes are harder to analyze.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  2. #17
    Registered User Dave++'s Avatar
    Join Date
    Jun 2007
    Location
    Where the Buffalo Roam
    Posts
    40

    Question animaniac?

    Quote Originally Posted by Brad0407 View Post
    I'm starting college in August and before I do, I want one more good challenge; something that combines the hardest parts of C++ programming with windows, DrectX and artificial intelligence. My idea is to create an Ant community(or any animal) where anyone could write a class with the artificial intelligence that would govern their ant's actions and compete with the other ants for survival.
    ...
    The would never have control over the laws of nature and simply change their coordinates. The world class would govern that as well as the animation.
    If anyone has a good reference on threads in C++, it would be appreciated. I will post any future questions about the C++ aspect of the project on this thread.
    Brad,
    What's your major in school? Have you seen Matrix Revolutions and its "the making of" on the companion CD?
    After working as an engineer for 15 years I was depressed for half a year after seeing the "Incredibles" because I realized there was more content in that movie than in most of the industry I was in. Even though it was a productive career I still perceived through that movie that both design and simulation efforts were finding their place in the animation industry.
    I even saw the transition first-hand going to SIGgraph regularly over the years in LA. One year it was low-key-cheap and the next high-end-glitz.
    I would definitely recommend you go to the nearest and earliest one possible and don't look back !

    http://www.siggraph.org/s2007/
    http://www.siggraph.org
    http://www.siggraph.org/publications...eview/SVR.html

    Regards,
    Dave
    Last edited by Dave++; 07-09-2007 at 05:23 PM.

  3. #18
    Registered User
    Join Date
    May 2007
    Posts
    88
    This may be a little off-topic on a C++ forum, but just at a first glance, this really sounds like a job for Stackless Python.

  4. #19
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    Haven't worked on it much but here's the start:
    Code:
    Version 1.cpp
    #include <windows.h>
    #include <windowsx.h>
    #include "application.h"
    
    
    int _stdcall WinMain(HINSTANCE instance, HINSTANCE prevInstance,
    					 LPSTR cmdLine, int showCmd)
    {
    	CApplication application;
    
    	application.SetInstance(instance);
    
    	if (!application.Initialize())
    		return 0;
    
    	return application.Run();
    }
    Code:
    Application.h
    #include <windows.h>
    #include <string>
    #include <ddraw.h>
    
    #define APPSTATE_UNINITIALIZED	0
    #define APPSTATE_INITIALIZED	1
    #define APPSTATE_RUNNING		2
    #define APPSTATE_DESTROYING		3
    #define APPSTATE_DESTROYED		4
    
    LRESULT _stdcall WindowProc(HWND window, unsigned int message,
    				WPARAM wParam, LPARAM lParam);
    
    class CApplication
    {
    	unsigned int state;
    	std::string className;
    	HWND mainWindow;
    	HINSTANCE mainInstance;
    	int windowX, windowY;
    	int windowWidth, windowHeight;
    	int displayWidth, displayHeight;
    	LPDIRECTDRAW7 ddrawObject;
    	LPDIRECTDRAWSURFACE7 primarySurface;
    	LPDIRECTDRAWSURFACE7 backSurface;
    
    public:
    	CApplication()
    	{
    		state = APPSTATE_UNINITIALIZED;
    	}
    
    	void SetInstance(HINSTANCE instance)
    	{ mainInstance = instance; }
    
    	bool Initialize();
    	int Run();
    };
    Code:
    Application.cpp
    #define INITGUID
    
    #include <windows.h>
    #include <windowsx.h>
    #include <string>
    #include <ddraw.h>
    #include "application.h"
    
    LRESULT _stdcall WindowProc(HWND window, unsigned int message,
    				WPARAM wParam, LPARAM lParam)
    {
    	switch(message)
    	{
    	case WM_DESTROY:
    		{
    			PostQuitMessage(0);
    			return 0;
    		} break;
    	default:break;
    	}
    	return DefWindowProc(window, message, wParam, lParam);
    }
    
    bool CApplication::Initialize()
    {
    	WNDCLASSEX windowClass;
    	RECT sizeInfo;
    	DDSURFACEDESC2 surfaceDesc;
    
    	if (!mainInstance)
    	{
    		MessageBox(0, "The main instance was never set", "Fatal Error", MB_OK);	
    		return false;
    	}
    
    	className = "Ants Version I";
    
    	windowClass.cbSize = sizeof(windowClass);
    	windowClass.style = CS_HREDRAW | CS_VREDRAW;
    	windowClass.lpfnWndProc = WindowProc;
    	windowClass.cbClsExtra = 0;
    	windowClass.cbWndExtra = 0;
    	windowClass.hInstance = mainInstance;
    	windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    	windowClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    	windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    	windowClass.hbrBackground = GetStockBrush(WHITE_BRUSH);
    	windowClass.lpszMenuName = NULL;
    	windowClass.lpszClassName = className.data();
    
    	if (!RegisterClassEx(&windowClass))
    	{
    		MessageBox(0, "Failed to register the class", "Fatal Error", MB_OK);		
    		return false;
    	}
    
    	windowX = 30;
    	windowY = 30;
    	displayWidth = 800;
    	displayHeight = 600;
    
    	sizeInfo.top = windowY;
    	sizeInfo.left = windowX;
    	sizeInfo.right = displayWidth + windowX;
    	sizeInfo.bottom = displayHeight + windowY;
    
    	if (!AdjustWindowRect(&sizeInfo, WS_OVERLAPPEDWINDOW, false))
    	{
    		MessageBox(0, "Could not get window dimensions", "Fatal Error", MB_OK);		
    		return false;
    	}
    
    	windowWidth = sizeInfo.right - sizeInfo.left;
    	windowHeight = sizeInfo.bottom - sizeInfo.top;
    
    	mainWindow = CreateWindowEx(NULL, className.data(), className.data(),
    								WS_OVERLAPPEDWINDOW, sizeInfo.left, sizeInfo.top, 
    								windowWidth, windowHeight,
    								NULL, NULL, mainInstance, NULL);
    
    	if (!mainWindow)
    	{
    		MessageBox(0, "Could not create the window", "Fatal Error", MB_OK);
    		return false;
    	}
    
    	ShowWindow(mainWindow, SW_SHOWNORMAL);
    	UpdateWindow(mainWindow);	
    
    	if (FAILED(DirectDrawCreateEx(NULL, (void **)&ddrawObject, IID_IDirectDraw7, NULL)))
    	{
    		MessageBox(0, "Could not create the ddraw interface", "Fatal Error", MB_OK);
    		return false;
    	}	
    
    	if (FAILED(ddrawObject->SetCooperativeLevel(mainWindow, DDSCL_NORMAL)))
    	{
    		MessageBox(0, "Could not set the cooperative level", "Fatal Error", MB_OK);
    		return false;
    	}	
    
    	memset(&surfaceDesc, 0, sizeof(surfaceDesc));
    	surfaceDesc.dwSize = sizeof(surfaceDesc);
    	surfaceDesc.dwFlags = DDSD_CAPS;
    	surfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;	
    
    	if (FAILED(ddrawObject->CreateSurface(&surfaceDesc, &primarySurface, NULL)))
    	{
    		MessageBox(0, "Failed to create the primary surface", "Fatal Error", MB_OK);
    		return false;
    	}
    
    	if (FAILED(primarySurface->GetSurfaceDesc(&surfaceDesc)))
    	{
    		MessageBox(0, "Could not get the pixel format", "Fatal Error", MB_OK);
    		return false;
    	}
    
    	if (surfaceDesc.ddpfPixelFormat.dwRGBBitCount != 32)
    	{
    		MessageBox(0, "This application runs only in 32 bit mode", "Fatal Error", MB_OK);
    		return false;
    	}
    	
    	memset(&surfaceDesc, 0, sizeof(surfaceDesc));
    	surfaceDesc.dwSize = sizeof(surfaceDesc);
    	surfaceDesc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
    	surfaceDesc.dwWidth = displayWidth;
    	surfaceDesc.dwHeight = displayHeight;
    	surfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
    
    	if (FAILED(ddrawObject->CreateSurface(&surfaceDesc, &backSurface, NULL)))
    	{
    		MessageBox(0, "Failed to create the back surface", "Fatal Error", MB_OK);
    		return false;
    	}
    
    	state = APPSTATE_INITIALIZED;
    	return true;
    }
    
    int CApplication::Run()
    {
    	state = APPSTATE_RUNNING;
    
    	MSG msg;
    	DDBLTFX bltEffects;
    	RECT destRect;
    
    	memset(&bltEffects, 0, sizeof(bltEffects));
    	bltEffects.dwSize = sizeof(bltEffects);
    	bltEffects.dwFillColor = 0;
    
    	while (state == APPSTATE_RUNNING)
    	{
    		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    		{
    			if (msg.message == WM_QUIT)
    				state = APPSTATE_DESTROYED;
    
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    
    		destRect.left = 0;
    		destRect.top = 0;
    		destRect.right = displayWidth;
    		destRect.bottom = displayHeight;
    
    		if (FAILED(backSurface->Blt(&destRect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &bltEffects)))
    		{
    			MessageBox(0, "Failed to blit", "Fatal Error", MB_OK);
    			PostQuitMessage(0);
    			state = APPSTATE_DESTROYING;
    		}
    
    		destRect.left = windowX;
    		destRect.top = windowY;
    		destRect.right = displayWidth + windowX;
    		destRect.bottom = displayHeight + windowY;
    
    		if (FAILED(primarySurface->Blt(&destRect, backSurface, NULL, DDBLT_WAIT, NULL)))
    		{
    			MessageBox(0, "Failed to blit", "Fatal Error", MB_OK);
    			PostQuitMessage(0);
    			state = APPSTATE_DESTROYING;
    		}
    	}
    	state = APPSTATE_DESTROYING;
    
    	if (backSurface)
    	{
    		backSurface->Release();
    		backSurface = NULL;
    	}
    
    	if (primarySurface)
    	{
    		primarySurface->Release();
    		primarySurface = NULL;
    	}
    
    	if (ddrawObject)
    	{
    		ddrawObject->Release();
    		ddrawObject = NULL;
    	}
    
    	state = APPSTATE_DESTROYED;
    	return msg.wParam;
    }
    I apoligize to anyone who wanted to see DirectX 10 but I think DirectX 7 was the last version to truely support 2D graphics. I have 2 questions neither of which concern windows or directx, so don't boot me to the windows or game forum.

    1. I can't use WindowProc inside my class. Why and can I get around it? And will I be able to put my thread functions inside a class? (ok, a little to with windows)

    2. Is there a C++ function comparable to memset or ZeroMemory that I could use?
    Don't quote me on that... ...seriously

  5. #20
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    1. WNDPROC is a pointer a C function. Thus it cannot point to member function. However, it can point to global C++ or static member functions (at least on Win32 - actually, it can only point to extern "C" functions, as far as the standard is concerned, and at least one compiler I know enforces this with appropriate command line options).
    If you want to use a member function instead, you need a stub. Then you can use any sort of trickery to get at a class instance and call a member. Typical solutions include window-specific data (Get/SetWindowLongPtr with WM_USERDATA or positive offsets, used mostly by small homegrown systems), global or thread-specific maps between window handles and instance pointers (MFC) or dynamically generated per-window procedures with the instance pointer hard-coded (so-called thunks; I think ATL does that).

    2. There's std::fill, which is probably implemented using memset for PODs. But memset is fine, really. Include cstring, not string.h, and call it as std::memset, and it's perfectly valid C++.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #21
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    I got WndProc to work as a class member with the ability to access member variables:
    Code:
    LRESULT _stdcall CApplication::WindowProc(HWND window, unsigned int message,
    				WPARAM wParam, LPARAM lParam)
    {
    	CApplication* app;
    	switch(message)
    	{
    	case WM_CREATE:
    		{
    			SetWindowLong(window, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);			
    		} break;
    		
    	case WM_DESTROY:
    		{
    			app = (CApplication*)GetWindowLong(window, GWL_USERDATA);
    			MessageBox(window, app->className.data(), "Terminating", MB_OK);
    			PostQuitMessage(0);
    			return 0;
    		} break;
    	default:break;
    	}
    	return DefWindowProc(window, message, wParam, lParam);
    }
    bool CApplication::Initialize()
    { 
    ...
    	mainWindow = CreateWindowEx(NULL, className.data(), className.data(),
    								WS_OVERLAPPEDWINDOW, sizeInfo.left, sizeInfo.top, 
    								windowWidth, windowHeight,
    								NULL, NULL, mainInstance, this);
    ...
    }
    Code:
    class CApplication
    {
    	...
    public:
    	...
    	static LRESULT _stdcall WindowProc(HWND window, unsigned int message,
    				WPARAM wParam, LPARAM lParam);
    };
    Don't quote me on that... ...seriously

  7. #22
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    A more common way is to initialize the window data in WM_NCCREATE (it is the very first message sent) and just forward all other messages to a proper member wndproc.
    Code:
    class CApplication
    {
    	...
    	static LRESULT _stdcall WindowProcStub(HWND window, unsigned int message,
    				WPARAM wParam, LPARAM lParam);
    	LRESULT WindowProc(unsigned int message, WPARAM wParam, LPARAM lParam);
    };
    Code:
    LRESULT _stdcall CApplication::WindowProcStub(HWND window, unsigned int message,
    				WPARAM wParam, LPARAM lParam)
    {
    	CApplication* app;
    	if(message == WM_NCCREATE) {
    		app = static_cast<CApplication*>(
    			reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
    		app->m_hwnd = window;
    		SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(app));
    	} else {
    		app = reinterpret_cast<CApplication*>(
    			GetWindowLongPtr(window, GWLP_USERDATA));
    	}
    
    	if(app) {
    		return app->WindowProc(message, wParam, lParam);
    	} else {
    		return DefWindowProc(window, message, wParam, lParam);
    	}
    }
    It's mostly a matter of taste, but note these two points:
    1) Some messages are sent before WM_CREATE, such as WM_SIZE and WM_MOVE. If you set the window data in WM_CREATE, you can't access the object from these.
    2) Always use Get/SetWindowLongPtr when dealing with pointers. The other is not 64-bit safe.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #23
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    Quote Originally Posted by CornedBee View Post
    It's mostly a matter of taste, but note these two points:
    1) Some messages are sent before WM_CREATE, such as WM_SIZE and WM_MOVE. If you set the window data in WM_CREATE, you can't access the object from these.
    2) Always use Get/SetWindowLongPtr when dealing with pointers. The other is not 64-bit safe.
    Ok. Thanks. I figured that there were some possible improvements to my code given that most search results on the web about this subject came up with crazy ideas, no code, and a few people who said it was impossible without assembler. The thing I'm debating on right now is whether to my the window and graphics parts of the application separate classes or just separate them into their own functions. Is there a good rule of thumb about the size of classes?
    Don't quote me on that... ...seriously

  9. #24
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    No. You split into classes according to purpose, not size of the code.

    A window is distinct from its controlling application, so putting the window handling in CApplication is probably a bad idea.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #25
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    I'm trying to put the graphics work in a separate thread, but my graphics calls fail. I'm running out of ideas why this won't work.
    Code:
    int CApplication::Run()
    {
    	int returnVal;
    	unsigned grphcsThrID;
    	state = APPSTATE_RUNNING;
    
    	HANDLE grphcsThrHandle = (HANDLE)_beginthreadex(NULL, 0, graphics.RunStub, (void*)(&graphics), 0, &grphcsThrID);
    
    	returnVal = graphics.window.Run();
    
    	WaitForSingleObject(grphcsThrHandle, INFINITE);
    	CloseHandle(grphcsThrHandle);
    	state = APPSTATE_DESTROYING;
    
    	graphics.Destroy();
    
    	state = APPSTATE_DESTROYED;
    	return returnVal;
    }
    
    unsigned __stdcall CGraphics::RunStub(void* arg)
    {
    	CGraphics *grphc = (CGraphics*)arg;
    	return grphc->Run();
    }
    
    unsigned CWindow::Run()
    {
    	state = APPSTATE_RUNNING;
    	MSG msg;
    
    	while (state == APPSTATE_RUNNING)
    	{
    		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    		{
    			if (msg.message == WM_QUIT)
    				state = APPSTATE_DESTROYED;
    
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    	}
    	
    	return msg.wParam;
    }
    
    int CGraphics::Run()
    {
    	state = APPSTATE_RUNNING;
    
    	while (state == APPSTATE_RUNNING && window.state == APPSTATE_RUNNING)
    	{
    		DDBLTFX bltEffects;
    		RECT destRect;
    
    		memset(&bltEffects, 0, sizeof(bltEffects));
    		bltEffects.dwSize = sizeof(bltEffects);
    		bltEffects.dwFillColor = RGB32(0, 100, 255);
    
    		destRect.left = 0;
    		destRect.top = 0;
    		destRect.right = window.displayWidth;
    		destRect.bottom = window.displayHeight;
    
    		if (FAILED(backSurface->Blt(&destRect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &bltEffects)))
    		{
    			MessageBox(0, "Failed to colorfill", "Fatal Error", MB_OK);
    			PostQuitMessage(0);
    			state = APPSTATE_DESTROYING;
    		}
    
    		destRect.left = window.displayX;
    		destRect.top = window.displayY;
    		destRect.right = window.displayWidth + window.displayX;
    		destRect.bottom = window.displayHeight + window.displayY;
    
    		if (FAILED(primarySurface->Blt(&destRect, backSurface, NULL, DDBLT_WAIT, NULL)))
    		{
    			MessageBox(0, "Failed to blit", "Fatal Error", MB_OK);
    			PostQuitMessage(0);
    			state = APPSTATE_DESTROYING;
    		}
    	}
    
    	window.state = APPSTATE_DESTROYING;
    
    	return 0;
    }
    Don't quote me on that... ...seriously

  11. #26
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Windows have thread affinity. You should only make calls to windowing functions from the thread that created the window.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #27
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    Quote Originally Posted by CornedBee View Post
    Windows have thread affinity. You should only make calls to windowing functions from the thread that created the window.
    That's what I figured when PeekMessage only worked in the main thread. I was just hoping that DirectX would be different.
    Don't quote me on that... ...seriously

  13. #28
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Nope.

    The rule is to leave the UI stuff (mainly graphics) in the main thread, and if you have to, put other, non-UI tasks in worker threads.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #29
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    Version II is complete:
    Changes:
    1. Class for graphics and window
    2. Changed background color
    3. WndProc is a class member
    4. Locked frame rate at 60 FPS
    5. Window can no longer be resized or maximized.
    6. Window can be moved and deactivated without glitches.

    The code is getting very long so I'll just upload the files and a screenshot.
    Don't quote me on that... ...seriously

  15. #30
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    And the final 3 files:
    Don't quote me on that... ...seriously

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. AI Challenge
    By unknown_111 in forum General AI Programming
    Replies: 0
    Last Post: 10-02-2007, 12:18 AM
  2. Programming Challenge (for my school)
    By Ezerhorden in forum C++ Programming
    Replies: 2
    Last Post: 01-04-2006, 06:56 AM
  3. Calc challenge
    By cerin in forum C++ Programming
    Replies: 5
    Last Post: 02-06-2005, 04:57 PM
  4. Challenge?
    By Mackology101 in forum C Programming
    Replies: 5
    Last Post: 11-23-2004, 02:30 PM
  5. Requesting a challenge
    By RealityFusion in forum C++ Programming
    Replies: 8
    Last Post: 08-18-2003, 08:24 PM