Compiler Error C2981, has any one gotten around it?

C2981 : the dynamic form of ‘keyword’ is not supported with /kernel

To give a jist:

  1. Assume there are two C++ interfaces I1 and I2
  2. Assume a class C which implements both of them, class C : public I1, public I2
  3. The consumer of the class object only has view of I1 and has a pointer to it called pI1
  4. Now it wants to also use I2, and tries to do a dynamic cast if(dynamic_cast<I2*>( pI1 ) != NULL)…
  5. the compiler throws this error stating this form of dynamic cast isn’t supported with the /Kernel option!

A good and simple example of doing this in user mode is here: https://stackoverflow.com/questions/1023511/multiple-interfaces-inhertience-casting-from-one-to-another/1023517
and a good explanation here : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1105r0.html (search the keyword C2981)

Out of curiosity, I tried reinterpret_cast, which obviously doesn’t work, and the subsequent use of the vtable lands in random functions inside the object.

Short of building a COM like QI interface mechanism, is there a workaround?

I do not want to expose the underlying object instead of the interface, as that will require changes in many places inside the driver, neither do I like the visitor pattern described in the stackoverflow page.

If there is another elegant solution to this one, kindly suggest.

All of the port/miniport models that use C++ use a COM-like QueryInterface mechanism. I’ve always thought dynamic_cast was a huge hack.

@Tim_Roberts said:
All of the port/miniport models that use C++ use a COM-like QueryInterface mechanism. I’ve always thought dynamic_cast was a huge hack.

looks like I have to redesign a lot ground up, implementing the QI for all classes giving them something like IUknown as base and object IDs, going to be a fun weekend :neutral:

Why are you bothering with all of this? Creating interfaces is valid when you are planning for future flexibility - especially by others - which should almost never be the case for KM code for a specific driver. I don’t like C++ at all and prefer either C or C#, but I understand why people use it. but don’t over use it right? And my biggest problem with C++ is how easy it is to over use the ‘features’ and how many more things need to be checked when reviewing code (which is what I do almost exclusively)

Why are you bothering with all of this?

We’ve been through these religious argument for 15 years or more, and frankly I’m tired of it. Whatever your prejudices against C++ are, they are just silly. C++ is a tool, and it is a powerful one at that. It works perfectly well in the kernel, within the well-known limitations. Many of the streaming drivers require it. Bad programmers write bad code in any language. Presumably, you understand that any C# code you write is just calling C++ native routines underneath.

1 Like

I’m just as tired of defending my position over those same years and will not do it again. as you say, all of the arguments have been said and everyone has already made up their minds already. this is a legitimate technical question to the OP and not an attempt to reignite some long settled debate.

C# code does not in general call out to C++ code. It can, but these days even the C# compiler is written in C#

@MBond2 said:
this is a legitimate technical question to the OP and not an attempt to reignite some long settled debate.

in that case let me attempt to answer it atleast…

@MBond2 said:
Why are you bothering with all of this? Creating interfaces is valid when you are planning for future flexibility - especially by others - which should almost never be the case for KM code for a specific driver. I don’t like C++ at all and prefer either C or C#, but I understand why people use it. but don’t over use it right? And my biggest problem with C++ is how easy it is to over use the ‘features’ and how many more things need to be checked when reviewing code (which is what I do almost exclusively)

What if one as an entire kernel echo system to support, with different drivers having different life spans running together, , some updates more frequently than others, and need to communicate with each other via refcounted objects and these objects can have different versions based on when they were built and need to be backward compatible as well. Imagine an entire COM like ecosystem designed for the kernel, where objects are passed with interface pointers and use queryInterface to find other interfaces in run time.

That is my use case, and it is very hard to get all this done in C, it can be, but I don’t think it will be justifiable.

What if one as an entire kernel echo system to support, with different drivers having different life spans running together, , some updates more frequently than others, and need to communicate with each other via refcounted objects and these objects can have different versions based on when they were built and need to be backward compatible as well. Imagine an entire COM like ecosystem designed for the kernel, where objects are passed with interface pointers and use queryInterface to find other interfaces in run time.

Hmmm… You have just described the Windows OS kernel-mode architecture.

Get your last comments in, folks…, I’m going to lock this thread soon.

Peter

@“Peter_Viscarola_(OSR)” said:
Hmmm… You have just described the Windows OS kernel-mode architecture.

Well, not as big at that, but with 9 drivers. Also Windows was designed before C++, so a lot of the legacy design is baked into it, even though I speculate, I have no idea whether things have changed internally, while they kept the interfaces c style, but probably not.

This product was written much later, and has gone considerable rewrites over the years and moved to c++/COM like design.

As some of you might recall, I had taken a 10+ years hiatus from kernel coding and have recently returned back to it roughly a year ago, When I saw that code, it looked nothing like kernel code to me, and it was a tough and a steep uphill climb for me.

As with any system, once you get used to it, you tend to understand the philosophy of the original designers, and I have to admit, having those nifty helper objects, and knowing how to use them correctly again, has increased by efficiency and it is much easier to do quality checks as well.

They also invested a lot on unit tests, and porting the code to be testable in user land with bullseye and other tools, which makes it easier to check what paths haven’t been tested. These abstractions also help newer programmers onboard easily, the boiler plate kernel stuff being hidden away deep inside the trenches. This makes hiring easy(it isn’t easy to get kernel engineers).

Keep in mind: C++ is just a TOOL.

I literally meant that every concept you listed is part of Windows kernel-mode architecture, even if there were no C++ in the entire Windows system. I admit that the the “where objects are passed with interface pointers and use queryInterface to find other interfaces in run time” is pretty limited in Windows… but we DO exactly have such an interface, don’t we.

Peter

@“Peter_Viscarola_(OSR)” said:
Keep in mind: C++ is just a TOOL.

but we DO exactly have such an interface, don’t we.

if you are using wdf, but not all drivers are WDF based. BTW, object based design is possible in C as well, and Ctor() Dtor() concepts are very much possible with some glue like code and some scripts.

I am not denying it, and yes C++ is just a tool, I was merely stating the ease of use of the tool in a programmers life. Yes, the tool is as good as it’s handler, agreed, and kernel needs special care, like stack bloat, pool allocation etc.

you are using wdf, but not all drivers are WDF based

No… not just in WDF. IoGetDeviceInterfaces.

object based design is possible in C as well

Of course! And many other such principles.

Peter

OP, Does something like this help?

http://jumpdollar.blogspot.com/2021/04/code-simple-rtti.html