NULL promoted to class

NULL promoted to class

I was performing a major re-write of some code and ran into one of those situations where looking at the code did not did not convey anything was wrong with it.

The former code had a class that contained pointers to some objects, and the ctor of that class nulled out the pointers.

fee = NULL;
fi = NULL;
fo = NULL;

Where NULL is promoted to pointer of any type.

In the rewrite of the code the pointers were replaced with the class objects using same names in the code.
However, a programming error on my part, I left the former pointer NULL-ing code in the ctor.

The compiler did not produce an error, I do not know if it should or should not, but the result was:

The compiler promoted the NULL to the class object,
peformed a default copy operator,
then destroyed the NULL class object.

Is this a feature or bug???

Jim Dempsey

www.quickthreadprogramming.com
25 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

If you can show us a small test case that would help. NULL is usuallya macro for (void *)0 which can be converted to any pointer type. You should not be able to assign NULL to a class object unless there is anon explicitconstructorwith a pointer type argument or an appropriateconversion operator.Using the C++11new nullptr and nullptr_t typeis generally safer.

If the reference compiler (Gnu or Microsoft) doesn't give an error/warning then we cannot give oneeither. But it would be possible to add a remark if we can detect code that will likely lead to bad runtime behaviour.

Judy

I will see if I can make a simple reproducer (next year).

>>If the reference compiler (Gnu or Microsoft) doesn't give an error/warning then we cannot give oneeither.

Not so. you cannot get off that easy. If the code is wrong, you give the error.

The same argument above by you (Intel) can apply to the others as well.

Jim

www.quickthreadprogramming.com

Quoting jimdempseyatthecoveThe compiler promoted the NULL to the class object,
peformed a default copy operator,
then destroyed the NULL class object.

could you give more detail on the above? does your class have an overloaded operator to take "NULL" and convert to the class object? I could not duplicate it with a small test case:

cmd: icl /Od /EHsc t.cpp

#include 
using namespace std;

class B
{
public:
	B():b(10) {
	cout << "in B()" << endl;
	};
	~B() {
	cout << "in ~B()" << endl;
	};
private:
	int b;
};

class A
{
public: 
	A() { 
		p1 = NULL;
		cout << "in A(); P1= NULL" << endl;
	};
	~A(){
		if (p1 != NULL)
			delete p1;
		cout << "in ~A()" << endl;
	};
	void SetVars() {
		if (p1 == NULL)
		{
			cout << "in SetVars(): p1 = new B(); " << endl;
			p1 = new B();
		}
	}
private:
	B* p1;
};

int main(int argc, char *argv[])
{
  cout << "start" << endl;
  A *t1 = new A;
  cout << "allocated A object" << endl;
  t1->SetVars();
  delete t1;
  cout << "deleted A object" << endl;

  return 0;
}












  1. {
  2. coutlt;lt;"start"lt;lt;endl;
  3. AanA;
  4. coutlt;lt;"instantiatedan Aobject"lt;lt;endl;
  5. anA = NULL;
  6. coutlt;lt;"NULL'ed an Aobject"lt;lt;endl;
  7. return0;
  8. }

    I am not on my system, try the above. Your code did not assign NULL to an object.

    Jim

www.quickthreadprogramming.com

with this change, I got a compiler error:

C:>icl /Od /EHsc t.cpp

Intel C++ Compiler XE for applications running on IA-32, Version 12.1.1.258 Build 20111011

Copyright (C) 1985-2011 Intel Corporation. All rights reserved.

t.cpp

t.cpp(45): error: no suitable constructor exists to convert from "int" to "A"

t1 = NULL;

^

compilation aborted for t.cpp (code 2)

So icl did report an error for this simple case. is it the same for you with this testcase?

Jennifer

Your code produces the expected error (nosuitableconstructor operator).
My code did not.

I will see if I can make a simple reproducer.

A difference in my situation is that I used a class with a base class. The base class (I seem to recall) was the one getting ctor/dtor'd.

Jim

www.quickthreadprogramming.com

As a quick test, change your

class A {

to

class A : B {

and see what happens.

Jim

www.quickthreadprogramming.com

Still get the compilation error.

t.cpp(45): error: no operator "=" matches these operands

operand types are: A = int

t1 = NULL;

^

also attached the file t.cpp.

Jennifer

Attachments: 

AttachmentSize
Download t.cpp889 bytes

Quoting jimdempseyatthecove...
Is this a feature or bug???
...

Jim,could youprovide areal Test-Case that reproduces the problem?

I've created a very simple Test-Case and I was able to assign NULL to a class-based variable if I declare a
C++ operator '=' ( see Test-Case 3 ). The Test-Case is verified with four different C/C++ compilers.

My conclusion is as follows:

If a C/C++ compiler allows NULL assignment to some class-based automaticvariable, declared on the stack
or as global\staticand operator=( int ) is not declared, this is possiblya compiler'sbug.

...
class CTestA
{
public:
CTestA(){};
virtual ~CTestA(){};

// Test-Case 1
CTestA & operator=( CTestA &ta ){ return ( CTestA & )*this; };

// Test-Case 2
// CTestA & operator=( int &ia ){ return ( CTestA & )*this; };

// Test-Case 3
CTestA & operator=( int ia ){ return ( CTestA & )*this; };
};

class CTestB : public CTestA
{
public:
CTestB(){};
virtual ~CTestB(){};

// Test-Case 1
CTestB & operator=( CTestB &tb ){ return ( CTestB & )*this; };

// Test-Case 2
// CTestB & operator=( int &ib ){ return ( CTestB & )*this; };

// Test-Case 3
CTestB & operator=( int ib ){ return ( CTestB & )*this; };
};

class CTestC : CTestA
{
public:
CTestC(){};
virtual ~CTestC(){};

// Test-Case 1
CTestC & operator=( CTestC &tc ){ return ( CTestC & )*this; };

// Test-Case 2
// CTestC & operator=( int &ic ){ return ( CTestC & )*this; };

// Test-Case 3
CTestC & operator=( int ic ){ return ( CTestC & )*this; };
};
...

void main( void )
{
// MS C/C++ ( VS 2005 ) - Compiled
// Borland C++ v5.5.1 - Compiled
// MinGW v3.4.2- Compiled
// Turbo C++ v3.0.0 - Compiled if only one 'operator=( int ... )' is declared
// Failed if both operator=( int & ) and operator=( int ) are declared
// Error: 'CTestX::operator =(int)' cannot be distinguished from 'CTestX::operator =(int &)'

CTestA ta;
ta = NULL;
ta = ( int )NULL;
ta = ( int )0;
ta = ( int )0xFFFFFFFF;

CTestB tb;
tb = NULL;
tb = ( int )NULL;
tb = ( int )0;
tb = ( int )0xFFFFFFFF;

CTestC tc;
tc = NULL;
tc = ( int )NULL;
tc = ( int )0;
tc = ( int )0xFFFFFFFF;
}

Attached is a test case:

Place break in Foos::showBug() at statement

input = NULL;

Then run and use Step Into

Jim

Attachments: 

AttachmentSize
Download NULLtoClass.cpp12.57 KB
www.quickthreadprogramming.com

Bug shows with w_ccompxe_2011.7.258

Jim Dempsey

www.quickthreadprogramming.com

Hi Jim,
There is a new bug introduced today that prevented the attachment being uploaded. so I could not see your testcase.

could you paste the code to the response instead?

thanks,
Jennifer

Jennifer,

Hope this works.

// NULLtoClass.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include 

// ** Intel **
// The following are typedefs, defines, class, struct, etc...
// for missing headers in this shortened example program

typedef __int64 Interlocked_t;	// QuickThread.h
typedef short qtPlacement;	// QuickThread.h
struct qtControl { int	someStuff; void WaitTillDone() { return; } };	// QuickThread.h

namespace qt
{

struct parallel_manifold_token
{
	// member variables:
	void*	Context;			// User context
	void*	This;				// pointer to the data flow object
								// or
								// pointer to the resource flow object
	// member functions:
	// ctor
	parallel_manifold_token();
	// dtor
	~parallel_manifold_token();
};

struct parallel_manifold_context
{
	// member variables:
	void*						Context;		// User context
	Interlocked_t				maxTokens;		// maximum number of tokens permitted
	volatile Interlocked_t		nTokens;		// number of tokens currently in system
	parallel_manifold_token*	token_table;	// table of tokens
	
	// member functions:
	// ctor
	parallel_manifold_context();
	// dtor
	~parallel_manifold_context();
	// init function
	// sets User Context, maxTokens, allocates empty token_table[maxTokens]
	void init(void*	_Context, Interlocked_t _maxTokens);

	parallel_manifold_context(void*	_Context, Interlocked_t _maxTokens)
	{ init(_Context, _maxTokens); }

	void addToken(int i, void*	_Context, void* node);

	// retire token via address of some token in token_table[x]
	// user code pass parallel_manifold_token*
	void retireToken(parallel_manifold_token* token);
	void WaitTillDone();
};

struct token_proxy
{
	Interlocked_t x;	// magic number referencing token in parallel_manifold_context
};

struct parallel_resource_manifold;

struct parallel_manifold_base
{
public:
	// member variables:
	parallel_manifold_context*	context;
	parallel_manifold_token*	token_table;	// local copy of context->token_table
	Interlocked_t				ringBufferSize;
	token_proxy*				ringBuffer;
	volatile Interlocked_t		fillIndexABA;
	volatile Interlocked_t		emptyIndexABA;
	int							nServerThreads;
	qtPlacement					Placement;
	void (*server_f)(parallel_manifold_token*);
	parallel_resource_manifold*	resource;
	qtControl					control;
	bool						take_stats;
	__int64						nFills;			// updated when take_stats == true
	__int64						nTaskEnqueues;	// updated when take_stats == true

	// member functions:
	// ctor
	parallel_manifold_base();
	// dtor
	~parallel_manifold_base();
	// initializer
	void init( parallel_manifold_context* _context, parallel_resource_manifold*	_resource = NULL);
	// ctor with args
	parallel_manifold_base( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{
		init(_context,_resource);
	}
	void WaitTillDone() { control.WaitTillDone(); }
	static void fillShellStatic(parallel_manifold_base*);
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 0);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
}; // parallel_manifold_base

// Ring buffers are Ordered or FIFO
// Ordered:
//	Tokens may arrive in any order
//	Tokens are dispatched to the action task in circular sequence order
//	0, 1, 2, ..., n, 0, 1, 2, ..., n, 0, 1, 2, ..., n, ...
//
// FIFO:
//	Tokens may arrive in any order
//	Tokens are dispatched in arrival sequence order

// Single-Producer, Single-Consumer Ordered ring buffer
struct parallel_manifold_SPSC_Ordered : parallel_manifold_base
{
	parallel_manifold_SPSC_Ordered();
	parallel_manifold_SPSC_Ordered( parallel_manifold_context* _context );
	parallel_manifold_SPSC_Ordered( parallel_manifold_context* _context, parallel_resource_manifold* _resource );
	~parallel_manifold_SPSC_Ordered();
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 1);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};

// Single-Producer, Single-Consumer FIFO
struct parallel_manifold_SPSC_FIFO : parallel_manifold_base
{
	parallel_manifold_SPSC_FIFO() {;}
	parallel_manifold_SPSC_FIFO( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{ init(_context,_resource); }
	~parallel_manifold_SPSC_FIFO() {;}
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 1);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};

// Multi-Producer, Single-Consumer Ordered
struct parallel_manifold_MPSC_Ordered : parallel_manifold_base
{
	parallel_manifold_MPSC_Ordered() {;}
	parallel_manifold_MPSC_Ordered( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{ init(_context,_resource); }
	~parallel_manifold_MPSC_Ordered() {;}
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 1);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};

// Multi-Producer, Single-Consumer FIFO
struct parallel_manifold_MPSC_FIFO : parallel_manifold_base
{
	parallel_manifold_MPSC_FIFO() {;}
	parallel_manifold_MPSC_FIFO( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{ init(_context,_resource); }
	~parallel_manifold_MPSC_FIFO() {;}
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 1);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};

// Single-Producer, Multi-Consumer, FIFO
struct parallel_manifold_SPMC_FIFO : parallel_manifold_base
{
	parallel_manifold_SPMC_FIFO() {;}
	parallel_manifold_SPMC_FIFO( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{ init(_context,_resource); }
	~parallel_manifold_SPMC_FIFO() {;}
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 0);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};

// Multi-Producer, Multi-Consumer, FIFO
struct parallel_manifold_MPMC_FIFO : parallel_manifold_base
{
	parallel_manifold_MPMC_FIFO() {;}
	parallel_manifold_MPMC_FIFO( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{ init(_context,_resource); }
	~parallel_manifold_MPMC_FIFO() {;}
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 0);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};

#if 0
struct x : parallel_manifold_base
{
	x() {;}
	x( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )
	{ init(_context,_resource); }
	~x() {;}
	virtual void run(
		qtPlacement					_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 0);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
};
#endif

struct parallel_manifold_functor_token_list
{
	void (*server_f)(parallel_manifold_functor_token_list*);
	intptr_t	nTokens;
	parallel_manifold_token*	token_list[];
};

parallel_manifold_functor_token_list*
parallel_manifold_functor_token_list_new(
		void (*_server_f)(parallel_manifold_functor_token_list*),
		size_t _nTokens);

void parallel_manifold_functor_token_list_delete(parallel_manifold_functor_token_list* p);

struct resource_token_proxy : token_proxy
{
	parallel_manifold_base*	pmb;
};

struct parallel_resource_manifold
{
	// member variables:
	parallel_manifold_context*			context;
	parallel_manifold_token*			token_table;	// local copy of context->token_table
	Interlocked_t						ringBufferSize;
	resource_token_proxy*				ringBuffer;
	volatile Interlocked_t				fillIndexABA;
	volatile Interlocked_t				emptyIndexABA;
	bool								take_stats;
	__int64								nFills;			// updated when take_stats == true
	__int64								nTaskEnqueues;	// updated when take_stats == true
	parallel_resource_manifold();
	~parallel_resource_manifold();
	// init function
	void init(parallel_manifold_context* _context);
	virtual void fillShell();
	virtual void fill(parallel_manifold_token* t);
	virtual void fill(parallel_manifold_token* t, size_t order);
	virtual parallel_manifold_token* empty();
	parallel_manifold_token* empty(parallel_manifold_base* pmb);
}; // struct parallel_resource_manifold

struct parallel_multi_port_manifold_port
{
	parallel_manifold_context*	context;
	parallel_manifold_token*	token_table;	// local copy of context->token_table
	Interlocked_t				ringBufferSize;
};

struct parallel_multi_port_manifold_base
{

	parallel_manifold_context*	context;
	parallel_manifold_token*	token_table;	// local copy of context->token_table
	Interlocked_t				ringBufferSize;
	token_proxy*				ringBuffer;
	volatile Interlocked_t		fillIndexABA;
	volatile Interlocked_t		emptyIndexABA;
	int							nServerThreads;
	qtPlacement					Placement;
	void (*server_f)(parallel_manifold_token*);
	qtControl					control;

	bool						take_stats;
	__int64						nFills;			// take_stats
	__int64						nTaskEnqueues;	// take_stats

	parallel_multi_port_manifold_base();
	~parallel_multi_port_manifold_base();
	

	void init(
		parallel_manifold_context*	_context,
		qtPlacement	_Placement,
		void (*_server_f)(parallel_manifold_token*),
		int	_maxThreads = 0);
	
}; // struct parallel_multi_port_manifold_base

struct parallel_multi_port_manifold : parallel_multi_port_manifold_base
{ void*	vp; };

parallel_manifold_base::parallel_manifold_base()
{ return; }

parallel_manifold_base::~parallel_manifold_base()
{ return; }

void parallel_manifold_base::init(parallel_manifold_context * ctx, parallel_resource_manifold * mn)
{ return; }


void parallel_manifold_base::run(qtPlacement, void (__cdecl*fn)(struct qt::parallel_manifold_token *), int n)
{ return; }

void parallel_manifold_base::fillShell()
{ return; }


void parallel_manifold_base::fill(parallel_manifold_token * t, unsigned int n)
{ return; }

void parallel_manifold_base::fill(parallel_manifold_token *t)
{ return; }

parallel_manifold_token * parallel_manifold_base::empty()
{ return NULL; }

void parallel_manifold_SPSC_FIFO::run(qtPlacement, void (*fn)(parallel_manifold_token *), int n)
{ return; }
	
void parallel_manifold_SPSC_FIFO::fillShell()
{ return; }
	
void __thiscall qt::parallel_manifold_SPSC_FIFO::fill(parallel_manifold_token * t, unsigned int n)
{ return; }
	
void __thiscall qt::parallel_manifold_SPSC_FIFO::fill(parallel_manifold_token * t)
{ return; }

parallel_manifold_token *parallel_manifold_SPSC_FIFO::empty()
{ return NULL; }

} // namespace qt

using namespace std;
using namespace qt;

class FooManager {
	void*	stuff;
};

class Foos {
public:
	Foos(int ep, FooManager* fm);
	virtual ~Foos();

	void StartFooProcessing();
	void StopFoo();
	void writeBuffer(parallel_manifold_token* token);
	void showBug();
protected:
	// Use this for L7 Foo
	void SetupFoo(int ep);
	void FooProcess();

private:
	int 				endpoint;
	int 				numberToken;

public:
	FooManager* 		flowMgr;
	volatile int simulateNewPacket;
	// flow diagram:
//								(initialization)
//									  |
//									  |________
//									  V		      (top)
	parallel_manifold_SPSC_FIFO	input;//		|
//									  |-------->|
//									  V			|
	parallel_manifold_SPSC_FIFO	process;//		|
//									  ||------->|
//									  VV		|
	parallel_manifold_SPSC_FIFO	output;//		|
};

Foos::Foos(int n,class FooManager *fm)
{
	return;
}

Foos::~Foos()
{
	return;
}

void Foos::showBug()
{
	input = NULL;
}
int _tmain(int argc, _TCHAR* argv[])
{
	FooManager* flowMgr = new FooManager;
	Foos* flow = new Foos(0,flowMgr);
	flow->showBug();
	return 0;
}

Jim Dempsey

www.quickthreadprogramming.com

Yes, it helps.

It's because your class parallel_manifold_SPSC_FIFO has a constructor:

parallel_manifold_SPSC_FIFO( parallel_manifold_context* _context, parallel_resource_manifold* _resource = NULL )

so it can create an object from NULL. it does not like a bug to me.

Let me attach the simpler test here t.cpp.

Jennifer

Attachments: 

AttachmentSize
Download t.cpp889 bytes

That is aconstructor with two arguments. One required, one optional.

The required argument is (was) missing in the

input = NULL;

and there is no operator=()

both of which should have been rquired.

What this appears to have done is cast

(void*)0

into

(parallel_manifold_context*)0

a) Then assume an implied conversion operator. Or
b) implied ctor with argument of the point.

Actually I think in this case it would be better called a dereferencing copy operator.

There is no conversion operator (for a), and the syntax is not right to instigate a ctor.

An additional curiosity is the dtor is also run immediately after the assignment, therefore indicating the statement is performing an operator=() using a temporary object created from a (missing) conversion operator(parallel_manifold_SPSC_FIFO)(parallel_manifold_context*), also not specified.

To me, this seems like a bug.

Jim Dempsey

www.quickthreadprogramming.com

no, what the compiler did is : creating an object from "NULL" using ctor "MyClass(A* a, B* b=NULL)".

The strange thing is if I add the "A& operator=(A& in){}", both icl & cl emits an error below instead:

t.cpp(47): error: no operator "=" matches these operands

operand types are: B = int

bobj = NULL; // should error here

^

Jennifer

Quoting jimdempseyatthecove...
To me, this seems like a bug.
...
Jim Dempsey

Hello everybody,

I've just completedCompilations with 4 different C/C++ compilers. I'll do Runtime verifications( debugging ) tomorrow.

So, even a Turbo C++ v3.0.0 compiled the Test-Casewith somemodifications. Here is its output:

Turbo C++ Version 3.00 Copyright (c) 1992 Borland International
tcctes~1.cpp:
Turbo Link Version 5.0 Copyright (c) 1992 Borland International
Error: Undefined symbol parallel_manifold_base::run(short,void(far*)(parallel_manifold_token near*),int) in module tcctes~1.cpp
Available memory 3402856
TccTestApp -1 error(s), 0 warning(s)

Note: Please don't pay attention for a linking error. This is a different "story".

SUMMARY:

// MS C/C++ ( VS 2005 ) - Compiled \ Modifications - 0 \ One Warning

struct parallel_manifold_functor_token_list
{
...
parallel_manifold_token* token_list[];
// Warning C4200: nonstandard extension used : zero-sized array in struct/union
// Cannot generate copy-ctor or copy-assignment operator when UDT contains a zero-sized array
};

// Borland C++ v5.5.1 - Compiled \ Modifications - 3 \ No Warnings
//Doesn't support:
'intptr_t'
'__thiscall'

...
struct parallel_manifold_functor_token_list
{
void (*server_f)(parallel_manifold_functor_token_list*);
// intptr_t nTokens;
int nTokens;
parallel_manifold_token* token_list[];
};
...
void /*__thiscall*/ qt::parallel_manifold_SPSC_FIFO::fill(parallel_manifold_token * t, unsigned int n)
{ return; }

void /*__thiscall*/ qt::parallel_manifold_SPSC_FIFO::fill(parallel_manifold_token * t)
{ return; }
...

// MinGW v3.4.2 - Compiled \ Modifications - 2 \ No Warnings
//Doesn't support
'__thiscall'

...
void /*__thiscall*/ qt::parallel_manifold_SPSC_FIFO::fill(parallel_manifold_token * t, unsigned int n)
{ return; }

void /*__thiscall*/ qt::parallel_manifold_SPSC_FIFO::fill(parallel_manifold_token * t)
{ return; }
...

// Turbo C++ v3.0.0 - Compiled \ Modifications ->10\ No Warnings
//Doesn't support:
'iostream'
'__int64'
'namespace qt'
'qt::parallel_manifold_token'
'using namespace qt'
'using namespace std'
'bool'
'__thiscall'
'void parallel_manifold_functor_token_list_delete(parallel_manifold_functor_token_list* p)' commented
'void parallel_manifold_base::run(qtPlacement, void (__cdecl*fn)(struct /*qt::*/parallel_manifold_token *), int n)' commented ( creates a linking error )

>> That is aconstructor with two arguments. One required, one optional.

Correct. The trailing (second argument) is defaulted.

>> The required argument is (was) missing in the

>> input = NULL;

No. NULL is used for the required (first) argument and the default argument valueis used for the second.

>> and there is no operator=()

Every class has an implicit assignment operator generated by the compilerif the user does not declare one explicitly.

Hope you have read Judy's explaination.

Also I checked with our compiler FE experts,and below is my understanding when compiler see "input = NULL;"
1. "NULL" is a valid value for any pointer variable (as an init value).
2. compiler created a tempoary object with "NULL" based on ctor A(B*p, C*p2=NULL) ---- the 2nd parameter is optional.
3. compiler uses the "default assignment operator" generated by the compiler itself to assign the temp object to variable "input" when there is no assignment operator overloading.
4. in my testcase the overloading assignement operator is "B& operator (B& in)", but it should be "B& operator (B const & in)" instead. if changing to the later one, the compiler will not issue the error.

One rule worth note here from our compiler expert and this explains why "B& operator (B const & in)" is needed:

C++ overload resolution has a special rule: a reference-to-non-const parameter is not allowed to bind to a temporary object.

Jennifer

>>...
>>To me, this seems like a bug.
>>...
>>Jim Dempsey

I don't see any problems with C/C++ compilers I tested. I would rather change how 'input' member is
initialized. Here is an example:

...
void Foos::showBug()
{
// input = NULL;
input.init( NULL );
// input.init( NULL, NULL );
}
...

When you try to assign NULL to 'input' member two default C++ operators '='are called:

...
qt::parallel_manifold_SPSC_FIFO::operator=
...
004B8A5A call qt::parallel_manifold_base::operator= (4A55CDh)
...
004B8A75 ret 4
...

Here is a screenshot:

Here is a summary\outputs of my tests:

// Test-Case 1: Output without a C++ operator=( int ) in a parallel_manifold_SPSC_FIFO class

new FooManager
new Foos( 0, flowMgr )
Foos::Foos(int n,class FooManager *fm)
Foos::showBug()
input = NULL
parallel_manifold_SPSC_FIFO::parallel_manifold_SPSC_FIFO( ... )
parallel_manifold_base::init( ... )
delete flow
Foos::~Foos()
delete flowMgr

// Test-Case 2: Output if a C++ operator=( int ) added in a parallel_manifold_SPSC_FIFO class

new Foos( 0, flowMgr )
Foos::Foos(int n,class FooManager *fm)
Foos::showBug()
input = NULL
delete flow
Foos::~Foos()
delete flowMgr

...
struct parallel_manifold_SPSC_FIFO : parallel_manifold_base
{
...
parallel_manifold_SPSC_FIFO & operator=( int )
{
return ( parallel_manifold_SPSC_FIFO & )*this;
};
...
};
...

// Test-Case 3: Output if a call to input.init( ... ) added instead of input = NULL assignment

// Without a C++ operator=( int ) // With a C++ operator=( int )

new FooManager new FooManager
new Foos( 0, flowMgr ) new Foos( 0, flowMgr )
Foos::Foos(int n,class FooManager *fm)Foos::Foos(int n,class FooManager *fm)
Foos::showBug() Foos::showBug()
parallel_manifold_base::init( ... ) parallel_manifold_base::init( ... )
delete flow delete flow
Foos::~Foos() Foos::~Foos()
delete flowMgr delete flowMgr

They are identical and this is right.

...
void Foos::showBug()
{
CrtPrintf( RTU("Foos::showBug()\n") );
// CrtPrintf( RTU("input = NULL\n") );
// input = NULL;
input.init( NULL );
// input.init( NULL, NULL );
}
...

// Disassembler Codes - for Test-Case 1

...
qt::parallel_manifold_SPSC_FIFO::operator=
004B8A30 push ebp
004B8A31 mov ebp,esp
004B8A33 sub esp,0CCh
004B8A39 push ebx
004B8A3A push esi
004B8A3B push edi
004B8A3C push ecx
004B8A3D lea edi,[ebp-0CCh]
004B8A43 mov ecx,33h
004B8A48 mov eax,0CCCCCCCCh
004B8A4D rep stos dword ptr es:[edi]
004B8A4F pop ecx
004B8A50 mov dword ptr [ebp-8],ecx
004B8A53 mov eax,dword ptr [__that]
004B8A56 push eax
004B8A57 mov ecx,dword ptr [this]
004B8A5A call qt::parallel_manifold_base::operator= (4A55CDh)
004B8A5F mov eax,dword ptr [this]
004B8A62 pop edi
004B8A63 pop esi
004B8A64 pop ebx
004B8A65 add esp,0CCh
004B8A6B cmp ebp,esp
004B8A6D call @ILT+36765(__RTC_CheckEsp) (4ADFA2h)
004B8A72 mov esp,ebp
004B8A74 pop ebp
004B8A75 ret 4
...

Quoting Jennifer Jiang (Intel)1. "NULL" is a valid value for any pointer variable (as an init value).

[SergeyK] Correct. But in Jim's case this is not a problem.

2. compiler created a tempoary object with "NULL" based on ctor A(B*p, C*p2=NULL) ---- the 2nd parameter is optional.

[SergeyK] Did you debug the Jim's Test-Case? Or, you're simply assuming? '...temporary object...' of what type? It must be of some type if it really creates it!

3. compiler uses the "default assignment operator" generated by the compiler itself to assign the temp object to variable "input" when there is no assignment operator overloading.

[SergeyK] Two default C++ operators '=' are generated by an MSC/C++ compiler, for 'parallel_manifold_SPSC_FIFO' and 'parallel_manifold_base' classes.

4. in my testcase the overloading assignement operator is "B& operator (B& in)", but it should be "B& operator (B const & in)" instead. if changing to the later one, the compiler will not issue the error.

Untested abstract sketch

struct foo
{
char junk[1234];
foo() { junk[0] = 0; }
foo(char* x, char* y = NULL) { strncpy(junk, x, 1233); if(y) strncat(junk, y, 1233);}
};

no operator=()

foo A;
foo B;
foo C("test data"); //OK
foo D("test", " data"); // OK

A = B; // uses compiler generated operator=() OK
A = C; // same (with test data)
// I have no intention of doing any of the following
// the = NULL on next line was in the code when pointer was in use
A = NULL; // ought to report error
// this errors out, no suitable conversion operator
A = 12345;
// this does not error out
A = "abc"; // ought to report error
// the above behaved as if the following were issued
A = foo("abc");

Maybe I need to go back to programming school...
What this means is a strong type checked language (C++) is performing loosly type checked operations not intended. i.e. if I want the A="abc"; I would be required to supply the conversion operator. but apparently this is not the case.

Is there a way to force the compiler to generate an error as opposed to implicit ctoring a "faulty" statement?

Jim

www.quickthreadprogramming.com
Best Reply

If you declare a constructor "explicit" it can only be used for construction of an object, not for conversions.

Explanation here:

http://www.glenmccl.com/tip_023.htm

Is that what you want?

Judy

Yes Judy, that is exactly what I want.

IMVHO, I would rather that required "implicit A(int)" to enable the auto conversion operator, as opposed to "explicit A(int)" to disable it. But this is the way it is....

Thanks for the tip.

Jim Dempsey

www.quickthreadprogramming.com

Leave a Comment

Please sign in to add a comment. Not a member? Join today