6

I have two files: main.cpp and crypto.h containing the template class Crypto. In the class constructor I need to pass a function pointer and assign it to the (*keyGen) method. The function I need to pass has an optional param

template <class keyType>
class Crypto {
    private:
        keyType (*keyGen)(keyType);

    public:
        Crypto(keyType (*keyGen)(keyType)) {
            this->keyGen = keyGen;
        }

        void decode() {
            keyType foundKey;

            vector<string> output;
            keyType curKey = keyGen(); // Here keyGen has no args

            // if curKey does not decode the input
                curKey = keyGen(curKey); // Here keyGen has 1 arg of type keyType
            // else foundKey = curKey;

            // Save the decoded file
        }
};

In main.cpp

int keyGen(int key = -1) { // This is the optional param
    key++;
    return key;
}

int main() {
    // ...
    Crypto<int> crypto(keyGen);
    crypto.decode();
}

I need the decode method to be able to call keyGen with both no params or a keyType param. If keyGen is called with no params I need to return 0, else I need to return key+1. I thought about overloading the keyGen function, but since it is a function pointer it is not possible. I did a lot of research but I didn't find a solution.

Keyur Potdar
  • 7,158
  • 6
  • 25
  • 40

4 Answers4

3

If you want to have both keyGen() and keyGen(curKey) available, keyGen must be itself a function with default param. Note, that default value in signature does not change that signature - so pointer to int keyGen(int key = -1) must be of type int(*)(int).

You could add two member function of the same name, that internally call the function pointed to by keyGen with proper args:

template <class keyType>
class Crypto {
    ...
    void decode() {
        keyType foundKey;

        vector<string> output;
        keyType curKey = keyGenInternal(); // Here keyGen has no args

        // if curKey does not decode the input
        curKey = keyGenInternal(curKey); // Here keyGen has 1 arg of type keyType
        // else foundKey = curKey;
        // Save the decoded file
    }

private:
    keyType keyGenInternal() {
        return keyGen(-1);
    }

    keyType keyGenInternal(keyType key) {
        return keyGen(key);
    }

};

But for this to be a proper solution, you need to know the default value for the key - you can't simply put there -1, because for different key types this value may also be different. But you can make this value an additional template parameter:

template <class keyType, keyType defaultKey>
class Crypto {
    private:
        keyType keyGenInternal() {
            return keyGen(defaultKey);
        }

        keyType keyGenInternal(keyType key) {
            return keyGen(key);
        }
};

int main() {
    // ...
    Crypto<int, -1> crypto(keyGen);
    crypto.decode();
}

or pass it to constructor and store as a member:

template <class keyType>
class Crypto {
    private:
        keyType defaultKey;

    public:
        Crypto(keyType (*keyGen)(keyType), keyType default) : defaultKey(default) {
        this->keyGen = keyGen;
    }
};

int main() {
    // ...
    Crypto<int> crypto(keyGen, -1);
    crypto.decode();
}

I think it is the most straightforward solution.

Mateusz Grzejek
  • 11,698
  • 3
  • 32
  • 49
  • At the end I used the constructor version. I hoped that passing a function by pointer kept track of a default param value, but I see it is not possible. Thanks for the answer. – Damiano Scevola Feb 06 '18 at 18:19
3

You can use functor, i.e. a class where operator() is defined. Then your default value can be saved.

#include <vector>
#include <string>
#include <iostream>

struct KeyGenerator {
    using key_type = int;
    key_type operator() (key_type key = -1) {
        // If you have a key_gen() already implemented, just call it here
        // like: key_gen(key);
        // Here the random return value is only for demo purpose.
        return key == -1 ? 44444 : 12345 ;
    }
};

template <class KeyGen>
struct Crypto {
    Crypto(KeyGen keygen) : keygen_(keygen) {}

    void decode() {
        std::cout << keygen_() << "\n";
        std::cout << keygen_(2) << "\n";
    }

private:
    KeyGen keygen_;
};

int main() {
    Crypto<KeyGenerator> cry{ KeyGenerator{} };
    cry.decode();
}
llllllllll
  • 16,169
  • 4
  • 31
  • 54
  • OP says: **In the class constructor I need to pass a function pointer [...]. The function I need to pass has an optional param [...]**. For me it's clear, that keygen function may be an external one - your current solution requires copy-paste of whole key generation logic to a functor, which violates the assumptions. Perhaps `KeyGenerator` should be able to call a specified function instead of re-implementing its logic..? – Mateusz Grzejek Feb 02 '18 at 15:07
  • To be honest, this is a *demo* to show how a functor can be used to save a default value. The `return 44444;` is just for see the output as in a minimal example. If OP wants to use my approach, they should replace the `operator()` body by calling their own `keyGen` function. Only the parameter list and the default value needs to be copied. *Not* the inner logic. – llllllllll Feb 02 '18 at 15:14
  • @MateuszGrzejek Please considering remove the downvote if you think what I said make sense. Some comments added to the answer. – llllllllll Feb 02 '18 at 15:44
2

Another way to use functor would be to instantiate it inside the class template as a member.

template <class T>
struct Foo
{

    Foo( T defaultVal )
        :def(defaultVal)
    {

    }
    T operator()() const
    {
        /* some without param logic */
        return def - 1;
    }

    T operator()( T param ) const
    {
        /* some with param logic */
        return param;
    }

    private:
    T def;

};

template <class keyType>
class Crypto {
    private:
    Foo<keyType>  keyGen;
    public:
        Crypto( keyType defaultVal ) 
            :keyGen(defaultVal)
        {

        }

        void decode() {
            keyType foundKey;

            vector<string> output;
            keyType curKey = keyGen(); // Here keyGen has no args

            // if curKey does not decode the input
                curKey = keyGen(curKey); // Here keyGen has 1 arg of type keyType
            // else foundKey = curKey;

            // Save the decoded file
        }
};

Then,

int main() {
    // ...
    Crypto< int > crypto (-1) ;
    crypto.decode();
}
P0W
  • 46,614
  • 9
  • 72
  • 119
2

As default arguments do not take part in a function signature, function pointers can't be called with them.

To address this, you can create a class that wraps a KeyGen function along with its default parameter:

// wrap key generator in its own class, which you will be able to extend further
template <class Key>
struct KeyGen {
    using KeyGenCallable = Key (*)(Key);
public:
    constexpr KeyGen(KeyGenCallable kg, Key df) : 
      keyGen(kg), defaultKey(df) {}

    const Key& getDefaultKey() {
        return defaultKey;    
    }

    Key operator()() { // uses the default argument
        return (*keyGen)(defaultKey);
    }

    Key operator()(Key k) { // uses the argument you pass to it
        return (*keyGen)(k);
    }

private: 
    KeyGenCallable keyGen;
    Key defaultKey;
};

And then use it in your code instead of dealing with bare function pointers everywhere:

// same as before, but instead of receiving a bare function pointer, receives a KeyGen object that wraps it
template <class Key>
class Crypto {
    KeyGen<Key> keyGen;
    public:
        Crypto(KeyGen<Key> kg) : keyGen(kg) {}

        void decode() {
            Key foundKey;

            std::vector<std::string> output;
            Key curKey = keyGen(); // Here keyGen has no args

            if(true) {
                foundKey = keyGen(curKey);
            }   
            else {
                foundKey = curKey;   
            }
            std::cout << foundKey << "\n";
        }
};

int keyGenFuncInt(int key = -1) {
    key++;
    return key;
}

float keyGenFuncFloat(float key = -2.f) { // example of another keygen function
    key *= 2.f;
    return key;
}

int main() {
    KeyGen<int> keygen{ keyGenFuncInt, -1 };
    KeyGen<float> keygen2{ keyGenFuncFloat, -2.f }; // example of another keygen func

    Crypto<int> crypto(keygen);
    crypto.decode();
}

You can check a live example here.

amc176
  • 1,514
  • 8
  • 19