Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Mutex arrays 1

Status
Not open for further replies.

MacGyverS2000

Programmer
Jun 27, 2004
12
0
0
US
I've come across a sticky wicket. I inherited a spaghetti code program that had four mutexes spelled out explicitly.

In the public section of the class:
Code:
CMutex Mut1;
CMutex Mut2;
CMutex Mut3;
CMutex Mut4;

With the following empty constructor:
Code:
CFoo::CFoo():Mut1(FALSE, NULL, &Security),
             Mut2(FALSE, NULL, &Security),
             Mut3(FALSE, NULL, &Security),
             Mut4(FALSE, NULL, &Security)
{}

As the program will continue to increase in scope, I'm attempting to shortcut as much as possible by placing them into arrays.

The new mutex declaration:
Code:
Cmutex Mut[CONSTANT1][CONSTANT2];

It's the constructor I'm having issue with. I've tried the following:
Code:
CFoo::CFoo():Mut[CONSTANT1][CONSTANT2](FALSE, NULL, &Security)
{}

CFoo::CFoo():Mut[0][0](FALSE, NULL, &Security),
             Mut[0][1](FALSE, NULL, &Security),
             Mut[1][0](FALSE, NULL, &Security),
             Mut[1][1](FALSE, NULL, &Security)
{}

CFoo::CFoo():Mut[][](FALSE, NULL, &Security)
{}

CFoo::CFoo():Mut(FALSE, NULL, &Security)
{}
All are obviously incorrect (else this thread would be pointless). As I thought about it, though, I could not come up with any valid reason why the mutexes would need to be explicitly listed in the constructor in the first place.

So, I simply ended up with:
Code:
CFoo::CFoo()
{}
And I don't seem to have any issues.

Thoughts?
 
Alas, you (and me;) can't initialize non-static array member with non-default array element constructor via member initialization list of a class constructor.
CMutex default constructor has NULL 3rd arg (not &Security;).
In some circumstances it helps: try to define derived class (of CMutex in your case) with a proper default constructor (to inject non-trivial &Security 3rd arg for CMutex ctor). For example, it works for std::string:
Code:
struct dstring: public string
{
  dstring():string("My default initialization"){}
};
// and now...
class Junk
{
....
  dstring a[4]; // Junk() builds it with dstring ctor.
  void printa() const;
};
void Junk::printa()
{
  for (int i = 0; i < 4; ++i)
      cout << a[i] << endl; // it works like a sharm...
}
 
Ark, thanks for the reply. I had to read your reply multiple times to get the jist of what you're saying, though I have to admit I still don't comprehend all of it. I'm not an OOP guy, and I'll have to wait until mid-February before my 5-day crash-course in C++ training happens, so until then I'm trudging through as much as possible. In this case, I don't yet fully understand the format of the constructor. I get that Foo::Foo() says the first 'Foo' is the base class, etc., but I'm unfamiliar with following the constructor declaration with a third ':' and public variables. Without understanding the format, I'm grasping at straws as to why it's needed or how it should be modified. Google has remained my trusty friend ;-)

As I said in my original post, I've inherited a vast amount of spaghetti code that I'm trying to trim down to something more logical. At the moment, increasing functionality means "copy and paste variables, adding numerals to the end for uniqueness" ;-) I'm attempting to get away from that method by placing related items into arrays.

You've seen the mutexs above, which I have transformed into
Code:
Cmutex Mut[CONSTANT1][CONSTANT2];
This seems to be acceptable, but now I want to do something similar to the associated locks. Here's what they currently look like (names and constants edited for clarity):
Code:
CSingleLock Lock1(&theApp.pFooDoc->Mut[0][0]);
CSingleLock Lock2(&theApp.pFooDoc->Mut[0][1]);
CSingleLock Lock3(&theApp.pFooDoc->Mut[1][0]);
CSingleLock Lock4(&theApp.pFooDoc->Mut[1][1]);
Sometimes all of the locks are set/unset at once, other times they're used individually based upon variable values, so I'd like to do something like this:
Code:
NewLock[x][y].Lock()
and
NewLock[x][y].Unlock()
I can't figure out how to declare the locks initially, similar to:
Code:
CSingleLock NewLock[CONSTANT1][CONSTANT2](&Mut[0][0])
I know the above is incorrect, but it gives you an idea of what I'm trying to accomplish... declare a 2-D array of locks and set their pointers to the 2-D array of mutexes.
 
Ah, I believe I have figured out the format (at least partially). Here was the original constructor:

Code:
Foo::Foo():Mut1(FALSE, NULL, &Security),
           Mut2(FALSE, NULL, &Security),
           Mut3(FALSE, NULL, &Security),
           Mut4(FALSE, NULL, &Security)
{}
Everything after the third ':' essentially creates a separate constructor of each primitive type that runs through the main constructor. This allows you to create an instance of a primitive type (such as Mut1) with some default parameters (in this case, (FALSE, NULL, &Security) ).

If this is truly the case, then by removing those extra constructors from the end of Foo I'm creating a 2-D array of mutexes with default parameters. Since the default is (FALSE, NULL, NULL) and &Security ended up as essentially NULL in my case, it shows the same behavior.

Still, I'd like to know how it's properly done. At the moment, I'm still trying to figure out how to initialize the locks, similar to this idea:
Code:
for (int i = 0; i < 2; i++)
    for (int j = 0; j < 2; j++)
        CSingleLock	NewLock[i][j](&theApp.pFoo->Mut[i][j]);
 
Yes, my post was dedicated to array member initialization with default element constructor only.

In case where we need non-default constructors with individual arguments for every array element try approach with dynamic allocation of array of pointers to elements (add indirection level).

Add an array of pointers member and initialization codes in the class constructor body, for example:
Code:
class Foo
{
...
CSingleLock* m_plock[C1][C2];
...
};

Foo::Foo()
{
...
   for (int i = 0; i < C1; ++i)
   for (int j = 0; j < C2; ++j)
       m_plock[i][j] = new CSingleLock(&Mut[i][j]);
...
}
Now you may write (in member functions):
Code:
m_plock[i][j]->Lock(); // Unlock() etc
Don't forget add deallocation loops in Foo destructor:
Code:
Foo::~Foo()
{
...
   for (int i = 0; i < C1; ++i)
   for (int j = 0; j < C2; ++j)
       delete m_plock[i][j];
...
}
Of course, you may hide this extra indirection level in a proper member function (of Foo class).

Per se it's all-purpose method to build complex objects.

It seems (may be I'm wrong;) in synchronization area better use lock/unlock primitives on per object basis (add locks in every exclusive access object, not in external synch container). As usually you may have more clear and robust code...
 
Ark,

That's exactly what I was looking for... sometimes it's difficult to see the forest through the trees. After years of working with 2D arrays for graphics, I should have realized I needed to create/initialize each lock one at a time.

A star for you :)
 
Very pleased to help you.
Thank you for star.
Good luck!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top