Lambda "Injection/Inheritance/Override" function idea proprosal.

106 views
Skip to first unread message

Michael Davies

unread,
Jul 17, 2017, 9:09:25 AM7/17/17
to ISO C++ Standard - Future Proposals
Hey everyone,

I would like to hear your thoughts on an idea I've been toiling away over. Playing around with some code I was curious if I could try to create a small library filled with a few classes and override the default function behaviour of those classes. This was for test purposes so I could have a similar sort of design where I work if it was useful. Through experimenting I got something like this which is honestly not ideal:

#define TEST TestOverride::getInstance()

class TestOverride
{
public:

   
TestOverride() : winProcess([this](){ std::cout << "Standard winProcess: " << m_iWin << '\n'; })
   
{

   
}

   
~TestOverride() {}

   
static TestOverride& getInstance()
   
{
       
static TestOverride instance;

       
return instance;
   
}

   
template <typename T>
   
void OverrideWinProcess(T _Func)
   
{
        winProcess
= _Func;
   
}

   
void ProcessWins() const
   
{
        winProcess
();
   
}

   
int& getWin() { return m_iWin; }
   
int& getWinPos() { return m_iWinPos; }
   
bool& getFinishedProcessing() { return m_bFinishedProcessing; }

private:
   
int m_iWin;
   
int m_iWinPos;
   
bool m_bFinishedProcessing;

    std
::function<void()> winProcess;
};

#define WINPROCESS(x) []() \
{ \
   
int& m_iWin = TEST.getWin(); int& m_iWinPos = TEST.getWinPos(); bool& bFinishedProcessing = TEST.getFinishedProcessing(); x \
}

int main()
{
    TEST
.ProcessWins();

    TEST
.OverrideWinProcess(
    WINPROCESS
(
        m_iWin
+= 1;
        std
::cout << "Overloaded: " << m_iWin << '\n';
   
));

    TEST
.ProcessWins();

    TEST
.ProcessWins();

return 0;
}

What would be more ideal, is if I could create a new function without having to define and declare references to things within the class to make them available to the lambda. So a possible implementation may look something like this:

#define TEST TestOverride::getInstance()

class TestOverride
{
public:

   
TestOverride() : winProcess([this](){ std::cout << "Standard winProcess: " << m_iWin << '\n'; })
   
{

   
}

   
~TestOverride() {}

   
static TestOverride& getInstance()
   
{
       
static TestOverride instance;

       
return instance;
   
}

   
template <typename T>
   
void OverrideWinProcess(T _Func)
   
{
        winProcess
= _Func;
   
}

   
void ProcessWins() const
   
{
        winProcess
();
   
}



private:
   
int m_iWin
   
int m_iWinPos;
   
bool m_bFinishedProcessing;

    std
::function<void()> winProcess;
};

int main()
{
    TEST
.ProcessWins();

    TEST
.OverrideWinProcess(
    TEST
<- [](){
        m_iWin
+= 1;
        std
::cout << "Overloaded: " << m_iWin << '\n';
   
});

    TEST
.ProcessWins();

    TEST
.ProcessWins();
}

Where the lambda infers variable and function calls from whatever you point to. I thought about having something helpful like this to allow developers who work outside the library to be able to modify the library to suit their needs if the default options do not cover what they need to do.

Please let me know your criticisms  to this sort of idea.

Thanks,
Michael

Arthur O'Dwyer

unread,
Jul 17, 2017, 6:52:39 PM7/17/17
to ISO C++ Standard - Future Proposals


On Monday, July 17, 2017 at 6:09:25 AM UTC-7, Michael Davies wrote:
Hey everyone,

I would like to hear your thoughts on an idea I've been toiling away over. Playing around with some code I was curious if I could try to create a small library filled with a few classes and override the default function behaviour of those classes. This was for test purposes so I could have a similar sort of design where I work if it was useful. Through experimenting I got something like this which is honestly not ideal:


What would be more ideal, is if I could create a new function without having to define and declare references to things within the class to make them available to the lambda.

You already can! The only problem with your existing code is that you're trying to refer to private data members of your class from outside the class, which is fundamentally never allowed in C++ because it would break encapsulation. If you want those data members to be accessible by main(), then you need to make them public, or else make main() a friend of your class. Here's an example with "friend":



If you're not a friend of the class, then of course you can't access its private members; that's by design and will never, ever change.

HTH,
–Arthur

Michael Davies

unread,
Jul 17, 2017, 7:08:13 PM7/17/17
to ISO C++ Standard - Future Proposals
Hi Arthur,

Thanks for the response! In regards to this, I was thinking more along the lines of treating the lambda function as if you were actually defining a function definition within the class. So:

[] () {
m_iWin
+=1;

std
::cout << "Overloaded: " << m_iWin << '\n';
}

Would be more equivalent to treating the lambda function as if it were a direct function definition:

TestOverride::winProcess(){
    m_iWin
+= 1;
    std
::cout << "Overloaded: " << m_iWin << '\n';
}


Perhaps there should be some way of defining the lambda as if it were actually a function that was part of the class? Rather than those data members being accessible by main() they are only accessible within the very lambda you create rather than accessible by main().

Arthur O'Dwyer

unread,
Jul 18, 2017, 2:38:02 PM7/18/17
to ISO C++ Standard - Future Proposals
On Mon, Jul 17, 2017 at 4:08 PM, 'Michael Davies' via ISO C++ Standard - Future Proposals <std-pr...@isocpp.org> wrote:
Hi Arthur,

Thanks for the response! In regards to this, I was thinking more along the lines of treating the lambda function as if you were actually defining a function definition within the class. So:

[] () {
m_iWin
+=1;
std
::cout << "Overloaded: " << m_iWin << '\n';
}

Would be more equivalent to treating the lambda function as if it were a direct function definition:

TestOverride::winProcess(){
    m_iWin
+= 1;
    std
::cout << "Overloaded: " << m_iWin << '\n';
}


Perhaps there should be some way of defining the lambda as if it were actually a function that was part of the class? Rather than those data members being accessible by main() they are only accessible within the very lambda you create rather than accessible by main().

Remember, private members are not accessible to anyone except actual class members (and friends). Private members simply cannot be accessed by anyone who doesn't have permission to access them; that's the whole point of "accessibility" controls. If lambdas were allowed to break this rule, then every member of a class might as well be public. Example:

class Lockbox {
private:
    int m_secrets = 42;
};

int main() {
    Lockbox box;
    int secrets = [](Lockbox& b) { return b.m_secrets; }(box);
    assert(secrets == 42);
}

What you can do within the language, without breaking the rules, is make main() a friend of class Lockbox.
Or, you could change m_secrets to be public, in which case the above code would work fine.
Or, you could change m_secrets to be protected, and then derive from Lockbox (as long as it's not final); then you could get at the secrets of a WeakLockbox you created yourself, or with only a slight modicum of undefined behavior, get at the secrets of an arbitrary Lockbox. Example:

class Lockbox {
protected:
    int m_secrets = 42;
};

struct WeakLockbox : Lockbox {
    int& get_secrets() { return m_secrets; }

    // technically undefined behavior if you're acting on a Lockbox that isn't Weak
    static int& get_secrets(Lockbox& b) {
        return ((WeakLockbox&)b).m_secrets;
    }
};

IMHO, if you're doing this for purposes of unit-testing, you almost certainly should be using friend declarations; that's one of the very few cases where friend declarations are more useful than confusing.

class Lockbox {
private:
    int m_secrets = 42;
    friend class TestOverride;
};

–Arthur
Reply all
Reply to author
Forward
0 new messages