|
Hello Richard,
Re: Your code will be far more flexible if you use dynamic arrays, rather than fixed size ....
That will not work for this project. It is a telemetry project and the data arrives fast and furious. We are collecting data from very expensive missiles that travel very fast.
I tried using an std::map. It made writing the code much easier, but it was definitely far to slow. Too much overhead in the template libraries. When I was testing the std::map, I stepped into the code to fetch an integer. After about a hundred function calls to get that integer I knew that was the problem. The buffers from the sender were overflowing constantly. As soon as I deleted the map and went to a fixed size array, the cpu time of this application went down to about 1%. I started four copies for four different streams on one machine and had no problem.
The wstring is used when I am reading from a file as the tokenizer needs it. The data is saved in WCHAR (wchar_t) and for real time is used in that format.
So, for a re-cap. I want a declaration of N number of WCHAR strings as in:
WCHAR names[ STRING_COUNT ][ SIZE_OF_STRING ];
I have that working.
Then I want a procedure to take the address of that array of strings and hand it to a local pointer as in:
void Set_Local_Pointer( WCHAR * new_pointer[ STRING_COUNT][SIZE_OF_STRING ] )
{ mp_local_pointer = new_pointer; }
What is the right way to declare mp_local_pointer?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
The problem you have is that you cannot declare an array and then 'set it' to point to something else, it is not modifiable. The only way to do it (but I would not recommend it) is to use simple pointers, and let the code in your class figure out the length of each string in the array; in your case this is the fixed value SIZE_OF_STRING . So your code would need to be implemented like:
WCHAR* names;
void Set_Local_Pointer(WCHAR* new_pointer)
{
mp_local_pointer = new_pointer;
}
WCHAR names[ STRING_COUNT ][ SIZE_OF_STRING ];
wcscpy_s(names[0], L"StringZero");
wcscpy_s(names[1], L"StringOne");
wcscpy_s(names[2], L"StringTwo");
Set_Local_Pointer(names[0])
A better way would be to let the class object manage all the data by sending it the strings as a list, or with an index telling it which element to modify. something like:
BlahBlah class
{
WCHAR mpNames[STRING_COUNT][SIZE_OF_STRING];
void Set_Local_Pointer(WCHAR new_pointer)
{
memcpy(mp_local_pointer, new_pointer, STRING_COUNT * SIZE_OF_STRING);
}
void Set_Local_Pointer(WCHAR pLabel, int index)
{
memcpy(mp_local_pointer[index], pLabel, SIZE_OF_STRING);
}
|
|
|
|
|
The configuration class is to make changes to several data items needed by the AR2_Messages class. I don't think memcpy will provide the results needed. If I can do memcpy, then when can I not just set a pointer.
I have been nibbling around the edges but cannot quite get a full chomp on this cookie.
I have created a Console App project just to test the concept. While doing that I discovered that a Console App gets indigestion on WCHAR so I changed to wchar_t. Here is my best effort so far.
#include "stdafx.h"
const unsigned int COMMON_ARRAY_SIZE = 10;
const unsigned int MAX_NAME_SIZE = 34;
void Set_Pointer( wchar_t new_pointer[ COMMON_ARRAY_SIZE ][ MAX_NAME_SIZE ])
{
wchar_t local_pointer[ COMMON_ARRAY_SIZE ][ MAX_NAME_SIZE ];
wchar_t one[ MAX_NAME_SIZE ];
wchar_t two[ MAX_NAME_SIZE ];
local_pointer = new_pointer;
wcscpy_s( one, MAX_NAME_SIZE, local_pointer[0] );
wcscpy_s( two, MAX_NAME_SIZE, new_pointer[1] );
};
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t names[ COMMON_ARRAY_SIZE ][ MAX_NAME_SIZE ];
wcscpy_s( names[ 0 ], MAX_NAME_SIZE, L"[0] 123" );
wcscpy_s( names[ 1 ], MAX_NAME_SIZE, L"[1] 123" );
wcscpy_s( names[ 2 ], MAX_NAME_SIZE, L"[2] 123" );
wcscpy_s( names[ 3 ], MAX_NAME_SIZE, L"[3] 123" );
Set_Pointer( names );
return 0; }
The line marked as line 12 will not compile producing the following error:
Quote: Error 1 error C2440: '=' : cannot convert from 'wchar_t [][34]' to 'wchar_t [10][34]'
I note that there is NOT any variable/array declared as [][34]. Trying to convert from [][34] appears to be a misleading error message.
Not the line commented as line 15. When line 12 is commented out the code will copy to the local string using the argument pointer directly. So why does is refuse to set the local pointer, declared with exactly the same syntax? More important, how can I change to code to allow that pointer set?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
I explained in my previous message why line 12 will not compile, local_pointer is not a pointer it is an array. You cannot set an array equal to another array, you can only copy the content from one to another. Your use of the term pointer in some of your variable names is helping to confuse the issue, as they are not pointers.
So your code should be:
wchar_t local_array[ COMMON_ARRAY_SIZE ][ MAX_NAME_SIZE ];
wcscpy_s(local_array[0], MAX_NAME_SIZE, new_pointer[0]);
memcpy(local_array, new_pointer, COMMON_ARRAY_SIZE * MAX_NAME_SIZE);
If you want to use pointers then you must remember that all you are doing is keeping a pointer to the original data array in memory like:
wchar_t* local_pointer;
local_pointer = new_pointer;
So you must now ensure that that original data is not overwritten.
|
|
|
|
|
Well that was rather stupid on my part. This is what I want to accomplish in the procedure:
void Set_Pointer( wchar_t *new_pointer)
{
wchar_t *local_pointer;
wchar_t one[ MAX_NAME_SIZE ];
wchar_t two[ MAX_NAME_SIZE ];
wchar_t three[ MAX_NAME_SIZE ];
wchar_t four[ MAX_NAME_SIZE ];
local_pointer = new_pointer;
wcscpy_s( one, MAX_NAME_SIZE, local_pointer );
wcscpy_s( two, MAX_NAME_SIZE, local_pointer[1] );
wcscpy_s( three, MAX_NAME_SIZE, local_pointer[2] );
wcscpy_s( four, MAX_NAME_SIZE, local_pointer[3] );
};
The three lines that cannot convert have the error:
Quote: Error 1 error C2664: 'errno_t wcscpy_s(wchar_t *,rsize_t,const wchar_t *)' : cannot convert parameter 3 from 'wchar_t' to 'const wchar_t *'
The caller is to pass a pointer to an array defined in the manner
wchar_t some_array[ x ] [ y ];
where x and y are named constants.
What needs to be done?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
modified 4-Jul-14 7:23am.
|
|
|
|
|
local_pointer[1] is the second character in the array pointed to by local_pointer . I'm sorry to have to say this, but you really need to spend some time studying the differences between, and the uses of, pointers and arrays in C++.
|
|
|
|
|
|
Hello Richard,
I know I am trying your patience and I am quite grateful for your help.
I went back to your example and changed my code to match. That version works partially, but it does not use the pointer in the procedure to access the data. Look first and the call to the procedure in main and the two critical lines of code:
wchar_t names[ COMMON_ARRAY_SIZE ][ MAX_NAME_SIZE ];
Set_Pointer( &names[0][0] );
That is the only way I can get a good compile. Given that, here are the critical line from the procedure:
void Set_Pointer( wchar_t *new_pointer)
{
wchar_t *mp_local_pointer;
mp_local_pointer = new_pointer;
wcscpy_s( one, MAX_NAME_SIZE, mp_local_pointer );
wcscpy_s( two, MAX_NAME_SIZE, mp_local_pointer[1] );
...}
The first copy with source string mp_local_pointer produces the desired results. But there are more strings there to be accessed. The second line with
mp_local_pointer[1] will not compile. When the array dimensions are added in, I get back to where I started.
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
bkelly13 wrote: I get back to where I started. Hence the final comment in my previous answer.
The issue is that you are trying to copy data from a multi-dimensional array using a single dimension pointer; and given the structure of your original array you have no other choice. If you used the memcpy option which I suggested and just copy the entire array to a new array inside the class then you have your data correctly saved.
If you want to do it the hard way then you can do this:
wchar_t savedItems[COMMON_ARRAY_SIZE][MAX_NAME_SIZE];
wchar_t* localPointer;
localPointer = new_pointer;
for (int i = 0; i < COMMON_ARRAY_SIZE; ++i)
{
wcscpy_s(savedItems[i], MAX_NAME_SIZE, localPointer);
localPointer += MAX_NAME_SIZE; }
If you want to do it with individually named items then just copy them in order, remembering to add MAX_NAME_SIZE to localPointer after each copy.
|
|
|
|
|
Hello Richard,
Re:
The issue is that you are trying to copy data from a multi-dimensional array using a single dimension pointer; and given the structure of your original array you have no other choice.
It seems to me that what you are not explicitly saying is that we CAN NOT create a pointer to a two dimension array and pass that to another procedure. Is that correct?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
No, that's not what I'm saying. A pointer to an array of any size and number of dimensions, points to the elementary item of the array that it references. In your case, that item is a wchar_t so the pointer is defined as pointing to a simple array of characters; that is what gives rise to the problems you are having. If you defined your basic item as a structure rather than an array of characters, then you could make things easier. Something like:
struct dataArray
{
wchar_t item[ MAX_NAME_SIZE ];
} names[COMMON_ARRAY_SIZE];
Set_Pointer(names);
void Set_Pointer(dataArray* new_pointer)
{
dataArray* mp_local_pointer;
mp_local_pointer = new_pointer;
wcscpy_s(one, MAX_NAME_SIZE, dataArray[0].item); wcscpy_s(two, MAX_NAME_SIZE, dataArray[1].item); ...
}
By using a struct rather than a simple array the compiler knows the size of each structure (from its definition) and hence, how to access each individual item when using a pointer to the array of structures.
|
|
|
|
|
Here is my solution that demonstrates passing the pointer and gaining access to the array of strings:
#include "stdafx.h"
const unsigned int COMMON_ARRAY_SIZE = 10;
const unsigned int MAX_NAME_SIZE = 34;
struct st2_names
{
wchar_t names[ COMMON_ARRAY_SIZE ] [ MAX_NAME_SIZE ];
};
void Set_Pointer_2( st2_names *new_pointer );
int _tmain(int argc, _TCHAR* argv[])
{
st2_names names_2;
wcscpy_s( names_2.names[0] , MAX_NAME_SIZE, L"PITCH" );
wcscpy_s( names_2.names[1] , MAX_NAME_SIZE, L"ROLL" );
wcscpy_s( names_2.names[2] , MAX_NAME_SIZE, L"YAW" );
wcscpy_s( names_2.names[3] , MAX_NAME_SIZE, L"VELOCITY" );
Set_Pointer_2( &names_2 );
return 0;
}
void Set_Pointer_2( st2_names *new_pointer )
{
st2_names *mp_names_2;
mp_names_2 = new_pointer;
wchar_t t1[MAX_NAME_SIZE];
wchar_t t2[MAX_NAME_SIZE];
wchar_t t3[MAX_NAME_SIZE];
wchar_t t4[MAX_NAME_SIZE];
wcscpy_s( t1, MAX_NAME_SIZE, mp_names_2->names[0] );
wcscpy_s( t2, MAX_NAME_SIZE, mp_names_2->names[1] );
wcscpy_s( t3, MAX_NAME_SIZE, mp_names_2->names[2] );
wcscpy_s( t4, MAX_NAME_SIZE, mp_names_2->names[3] );
}
It is a but ugly, but does work. EDIT, I did simplify.
It does seem to me that you are saying: No, there is no way to create a simple multi-dimension array such as:
wchar_t names[ x ] [ y ];
and pass a pointer to a procedure, then use that pointer to access all the elements of the array.
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
modified 4-Jul-14 15:30pm.
|
|
|
|
|
bkelly13 wrote: It does seem to me that you are saying No, I have never said that. I have explained a number of different ways how you can use such a pointer, even though it may seem non-intuitive.
And I still think that you are making life more difficult for yourself by not using a simple memcpy to copy the entire structure, since that is all you want in the end result.
|
|
|
|
|
taking things a bit out of order.
The core code, C_AR2_Messages, hands the pointer over to C_Configuration_Manager. That code reads a text file and changes the names adding a prefix to them. When C_AR2_Messages resumes control, it queries another application (a vendor app) to acquire more information about the parameter. So memcopy to C_Configuration Manager does not work out.
Yes, you have given me hints and clues. But I cannot figure out how to change the example code to compile such that the pointer can be saved and used to read and write the array of strings.
I apologize for being rather slow, but do appreciate the time you spend trying to help me.
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
bkelly13 wrote: I apologize for being rather slow We all had to learn from the beginning to get where we are now. I couldn't begin to understand the basics of telemetry.
However, from your description above I have put together the following. It's making a few (possibly incorrect) assumptions about where the structure should be declared and whether it is the only one you use, but I hope it may give you something to work on.
struct st2_names
{
wchar_t names[ COMMON_ARRAY_SIZE ] [ MAX_NAME_SIZE ];
};
int C_Configuration_Manager(st2_names *new_pointer)
{
wcscpy_s(new_pointer->names[0], MAX_NAME_SIZE, L"PITCH" );
wcscpy_s(new_pointer->names[1], MAX_NAME_SIZE, L"ROLL" );
wcscpy_s(new_pointer->names[2], MAX_NAME_SIZE, L"YAW" );
wcscpy_s(new_pointer->names[3], MAX_NAME_SIZE, L"VELOCITY" );
return 0;
}
int C_AR2_Messages(st2_names *dataPointer)
{
C_Configuration_Manager(dataPointer);
CallOtherFunction(dataPointer);
return 0;
}
int wmain(int argc, wchar_t* argv[])
{
st2_names dataNames;
memset(&dataNames, 0, sizeof dataNames);
C_AR2_Messages(&dataNames);
return 0;
}
|
|
|
|
|
Hello,
If it was in the cards for you, you could easily deal with telemetry.
Good answer. That will work.
Additionally, I have been busy. The problem with my code was one of precedence. Here are, hopefully, the core lines that show how the procedure can be declared and defined.
const wchar_t prefix[ SIZE_OF ] = L"S01_";
void Set_Pointer( wchar_t (*new_pointer)[COUNT_OF][SIZE_OF] )
{
foo_names = new_pointer;
swprintf_s( (*foo_names)[0],SIZE_OF, L"%s%s", prefix, c0 );
}
Character * is a prefix and the postfix [] is bound first. The parenthesis causes the * to be bound to the name before the [].
Obviously, the fragment does nothing useful, but it shows what to do.
Putting it all in a structure is the easy way and I have used enough structures to have thought of that much earlier. Now I have both methods.
I really should print out a list of all the precedencies and put them on the wall next to me. With large type so I can read it without getting up.
There is a third way, using a friend class. Then it does not even need to be passed. (A tool to be used sparingly.) I tried that and failed. Time to hit the books and search engine and see what I did wrong there.
Thank you for your time and thank you for your patience.
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
Well worked out, I had forgotten about the precedence issue (but at my age I forget lots of stuff). Good luck with the rest of the work.
|
|
|
|
|
Windows 7, Visual Studio 2008, C++, MFC
I cannot cut and paste to this forum so please exclude minor typos.
Here is the first class declaration:
Class C_AR2_Messages
Public:
Class C_AR2_Messages();
~ Class C_AR2_Messages();
Private:
…
// friend void_C_Configuration_Manager( const C_AR2_Messages & );
// I found the template for this somewhere and presume that C_Configuration_Manager needs to be given the address of AR2_Messages so it can find variables declared private.
};
A fragment from the definition of C_AR2_Messages:
bool C_AR2_Messages::Configure_The_Application()
{
C_Configuration_Manager *Message_Configurator;
Message_Configurator = new C_Configuration_Manager;
… }
This all compiles as shown with no errors.
HOWEVER: when the friend line is uncommented out the compiler complains with the error:
Quote: C2061: ‘Message_Configurator’ : undeclared identifier.
It references that line so marked above. (Not the "friend" line in the declaration.) Since that is a pointer declaration I do not understand how the addition of the friend line causes it to become undeclared.
A google search returned what seems to be a good match for this problem, but I am on a military base and the firewall says that site is prohibited.
Will someone please enlighten me?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
modified 30-Jun-14 12:24pm.
|
|
|
|
|
It needs a forward declaration of void_C_Configuration_Manager class.
Do as follows.
class void_C_Configuration_Manager;
Do it in the header.
|
|
|
|
|
Windows 7, Visual Studio, C++, MFC dialog
A MFC dialog calls _beginthreadex and starts procedure Start_Server_As_Thread as a new thread.
That procedure instantiates class C_Server_Thread then calls a method that loops until an event indicates time to exit. That class creates a new instance of C_Log_Writer to write information to a log file.
The appropriate event is set, and the main loop exits.
Immediately on regaining control, procedure Start_Server_As_Thread deletes the class with:
delete mp_C_Server_Thread;
The destructor of that class is not called. The destructor contains the code
delete mp_C_Log_Writer;
which does not get run resulting in a memory leak.
I was under the impression that explicitly deleting an instance of a class forced the destructor to be called. What should I do to get that destructor called?
In the code that detects said event I can add a line to delete the logger class. Still, the question will remain, why is the destructor not called?
Edit:
Just for completeness, a silly mistake that I looked right at several times before finally stepping through the code one step at a time.
if( mp_C_Log_Writer == NULL )
{
delete mp_C_Log_Writer;
mp_C_Log_Writer = NULL;
The == should have been !=. Lesson learned again, always step through every line of code with the debugger at least once. Sorry to trouble anyone over this.
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
modified 29-Jun-14 16:55pm.
|
|
|
|
|
Is mp_C_Server_Thread of type C_Server_Thread* , or a base class? If it is abase class pointer, and the base class does not have a virtual destructor, the derived class destructor will not be called.
You are right, calling delete on a pointer will call the pointer type's destructor, if any, but without more code to look at it's very hard to say what the problem might be. For instance, are you absolutely sure that Start_Server_As_Thread does regain control, and that the delete call is executed?
|
|
|
|
|
Here is the procedure that is started as the new thread. It instantiates a class then runs the main method of that class.
unsigned int __stdcall Start_Server_As_Thread( void * p_void_pointer )
{
C_Server_Thread *mp_C_Server_Thread = NULL;
WCHAR m_log_file_text[ MAX_LOG_STRING_LENGTH ];
mp_C_Server_Thread = new C_Server_Thread( p_void_pointer );
mp_C_Server_Thread->Main_Thread_Loop();
delete mp_C_Server_Thread;
mp_C_Server_Thread = NULL;
_endthreadex( 0 );
return 0;
}
That class is declared as follows
class C_Server_Thread
{...}
The class it creates is declared:
class C_TCP_API_Server
{...}
Class C_Server_Thread manages all the overhead and communications between the main app and the thread. It instantiates C_TCP_API_Server, which does all the low level work of establishing a TCP/IP link with the client and sending the data.
Does that provide any useful information?
Edit: I continue to work this and am not certain where the problem is, but am becoming more certain that PEBKAC (Problem Exists Between Keyboard And Chair). It is looking more and more like a logic error in how I end this thing. Class C_Server_Thread could be in any of several states when it gets the event to exit. (Not connected to client, connected to client, sending data, etc) I need to work over the logic of what should be done for each state to ensure an orderly shutdown, not to mention complete shutdown.
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
modified 27-Jun-14 21:26pm.
|
|
|
|
|
Windows 7, Visual Studio 2008, C++
I am creating a console type application that uses events and an MFC dialog app just to test it. The app uses WaitForMultipleObjects() with the attendant array of events. The dialog will show the count of events set and detected. The code to update the dialog and show those counts begins like this:
void CTest_ServerDlg::OnBnClickedBtnUpdateThreadEvents()
{
const unsigned int NUMBER_SIZE = 8;
WCHAR text_value[ NUMBER_SIZE ];
int t;
t = m_common_data.thread_event_detects[ 0 ];
swprintf_s( text_value, NUMBER_SIZE, L"%03D", t );
m_thread_event_00.SetWindowTextW( text_value );
}
m_thread_event_00 is the name of the control variable assigned to the static text control in the dialog. Presume there are 16 events. I am new to Dialogs and their controls so that means there will be 16 static text controls, each with its own named variable.
The Question:
Can this setup be arranged so that the values can be updated via an iteration rather than copying the last three lines 15 more times and changing the thread_event_detects[ 0 ] and m_thread_event_00 to increment up to 15?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
|
|
|
|
|
Yes, you can use GetDlgItem()[^] on the static window's resource ID (returns CWnd* but you can cast that to whatever you need). As long as your resource IDs are contiguous, you can start at the first resource ID and increment the value as you iterate through them. Check the resource file for the actual numbers associated with the ID.
|
|
|
|
|
Windows 7 and XP, Visual Studio 2008, 2010, C++, MFC, Windows 32 application.
I am getting frustrated with the various types of strings. With all the articles I find and often opposing points of view, I wish to limit my options and seek your opinion.
I write telemetry code that must run quite fast and move much data. The strings I deal with are mostly to log data so I can see how the code works. The end product will be a windows application that really needs no user interface.
However, during development, and because a vendor uses MFC and C++, I wish to develop in that environment. I have no desire to explicitly exclude other operating systems, but I really do not expect to use anything other than Windows XP and Windows 7 any times soon.
Which option is best: WCHAR, wchar_t, CString, char[], or something else?
edit: I forgot that WCHAR is just a typedef of wchar_t. Which does not seem very bright. Would it be wise to drop all use of WHAR and replace with wchar_t?
Thank you for your time
If you work with telemetry, please check this bulletin board: www.irigbb.com
modified 13-Jun-14 11:08am.
|
|
|
|
|