issue 61 - SetArgPointee<N>("foo")

2,778 views
Skip to first unread message

Piotr Gorak

unread,
Nov 27, 2010, 9:27:20 AM11/27/10
to Google C++ Mocking Framework
I'm looking at gmock-actions.h, trying to figure out how to implement
Enhancement #61. The point is to have SetArgPointee take string
literal as the argument: SetArgPointee<N>("foo").

I've been trying to implement a specialized template for SetArgPointee
that would use the specific type of "char*" instead of the general
"typename T". And here comes the tricky part: I'm able to implement
this specialization for other types passed through pointer, for
example:

SetArgPointee<N>(int*) works just fine. But SetArgPointee<N>(char*)
does not compile. I found no other way to get it compiled than
commenting out the assignment assignment value_ = value in the general
template and making value_ non-const, which is obviously quite not
that I wanted.

Does anyone have an idea how this could be implemented properly? I was
thinking about replacing the current assignment-based implementation
with one that would use memcpy to copy the values, as this would work
with char arrays as well as with other objects. Any thoughts?

Piotr

Vlad Losev

unread,
Nov 27, 2010, 11:57:58 AM11/27/10
to Piotr Gorak, Google C++ Mocking Framework
Piotr,

Can you post your code and the errors that the compiler is giving you?

Vlad

Piotr Gorak

unread,
Nov 28, 2010, 4:08:40 AM11/28/10
to Google C++ Mocking Framework
In gmock-actions.h, I added:

+template<size_t N>
+class SetArgumentPointeeAction<N, const char*, false> {
+ public:
+ explicit SetArgumentPointeeAction(const char* str) {
+ str_ = new char[strlen(str)];
+ strncpy(str_, str, strlen(str));
+
+ }
+ template <typename Result, typename ArgumentTuple>
+ void Perform(const ArgumentTuple& args) const {
+ CompileAssertTypesEqual<void, Result>();
+ strncpy(::std::tr1::get<N>(args), str_, strlen(str_));
+ }
+ private:
+
+ char* str_;
+ GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
+};

+// The following version is DEPRECATED.
+template <size_t N>
+PolymorphicAction<
+ internal::SetArgumentPointeeAction<
+ N, char*, internal::IsAProtocolMessage<char*>::value> >
+SetArgumentPointee(char* x) {
+ return MakePolymorphicAction(internal::SetArgumentPointeeAction<
+ N, char*, internal::IsAProtocolMessage<char*>::value>(x));
+}

In test file:

EXPECT_CALL(cfg, setString(_))
.Times(1)
.WillOnce(SetArgumentPointee<0>("hello"));

The errors:

./Client_test.cpp: In member function ‘virtual void
ClientTest_CanIncreaseX_Test::TestBody()’:
./Client_test.cpp:20: warning: unused variable ‘str’
/home/piotr/svn-root/googlemock-turbo/include/gmock/gmock-actions.h:
In constructor ‘testing::internal::SetArgumentPointeeAction<N, A,
kIsProto>::SetArgumentPointeeAction(const A&) [with unsigned int N =
0u, A = char [6], bool kIsProto = false]’:
/home/piotr/svn-root/googlemock-turbo/include/gmock/gmock-actions.h:
1051: instantiated from
‘testing::PolymorphicAction<testing::internal::SetArgumentPointeeAction<N,
T, testing::internal::IsAProtocolMessage<T>::value> >
testing::SetArgumentPointee(const T&) [with unsigned int N = 0u, T =
char [6]]’
./Client_test.cpp:27: instantiated from here
/home/piotr/svn-root/googlemock-turbo/include/gmock/gmock-actions.h:
728: error: ISO C++ forbids assignment of arrays
/home/piotr/svn-root/googlemock-turbo/include/gmock/gmock-actions.h:
In member function ‘void
testing::internal::SetArgumentPointeeAction<N, A,
kIsProto>::Perform(const ArgumentTuple&) const [with Result = void,
ArgumentTuple = std::tr1::tuple<char*, std::tr1::_NullClass,
std::tr1::_NullClass, std::tr1::_NullClass, std::tr1::_NullClass,
std::tr1::_NullClass, std::tr1::_NullClass, std::tr1::_NullClass,
std::tr1::_NullClass, std::tr1::_NullClass>, unsigned int N = 0u, A =
char [6], bool kIsProto = false]’:
/home/piotr/svn-root/googlemock-turbo/include/gmock/gmock-actions.h:
379: instantiated from ‘typename
testing::internal::Function<F>::Result
testing::PolymorphicAction<Impl>::MonomorphicImpl<F>::Perform(const
typename testing::internal::Function<F>::ArgumentTuple&) [with F =
void ()(char*), Impl = testing::internal::SetArgumentPointeeAction<0u,
char [6], false>]’
./Client_test.cpp:35: instantiated from here
/home/piotr/svn-root/googlemock-turbo/include/gmock/gmock-actions.h:
734: error: invalid conversion from ‘const char*’ to ‘char’
make: *** [Client_test.o] Error 1

Piotr

Vlad Losev

unread,
Nov 29, 2010, 1:44:31 PM11/29/10
to Piotr Gorak, Google C++ Mocking Framework
Piotr,

You don't really need to specialize the SetArgumentPointee function. But you will need to provide a precise template specialization for the SetArgumentPointeeAction type it instantiates. Since the function strips const from the type, in order to copy literal stings, you'll need to provide a char array specialization such as this:

template <size_t N, size_t N2>
class SetArgumentPointeeAction<N, char[N2], false> {
 public:
  explicit SetArgumentPointeeAction(const char* value) : value_(value) {}

  template <typename Result, typename ArgumentTuple>
  void Perform(const ArgumentTuple& args) const {
    CompileAssertTypesEqual<void, Result>();
    strncpy(::std::tr1::get<N>(args), value_.c_str(), value_.size());
  }

 private:
  const string value_;
  GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
};

If you also want to use char* pointers, a la

const char* ps = "abc";
EXPECT_CALL(...).WillOnce(SetArgumentPointee<0>(ps));

you'll need to provide another specialization:

template <size_t N>
class SetArgumentPointeeAction<N, char*, false> {
 public:
  explicit SetArgumentPointeeAction(const char* value) : value_(value) {}

  template <typename Result, typename ArgumentTuple>
  void Perform(const ArgumentTuple& args) const {
    CompileAssertTypesEqual<void, Result>();
    strncpy(::std::tr1::get<N>(args), value_.c_str(), value_.size());
  }

 private:
  const string value_;
  GTEST_DISALLOW_ASSIGN_(SetArgumentPointeeAction);
};
HTH,
Vlad

Piotr Gorak

unread,
Nov 30, 2010, 3:29:27 PM11/30/10
to Google C++ Mocking Framework
Hi Vlad,

Yes, this helps - I tried the first option (with char[N2]) and it
worked. I added the copying of value_.c_str() + 1 characters (instead
of value_.c_str()) to get the string terminated with \0.

However, looking at your code sample (still, the first option) I don't
get how N2 automatically gets the value of literal string length, when
used in a test. This may be a silly question, but: how does this
happen?

Piotr

Vlad Losev

unread,
Nov 30, 2010, 4:41:49 PM11/30/10
to Piotr Gorak, Google C++ Mocking Framework
On Tue, Nov 30, 2010 at 12:29 PM, Piotr Gorak <pgo...@gmail.com> wrote:
Hi Vlad,

Yes, this helps - I tried the first option (with char[N2]) and it
worked. I added the copying of value_.c_str() + 1 characters (instead
of value_.c_str()) to get the string terminated with \0.

However, looking at your code sample (still, the first option) I don't
get how N2 automatically gets the value of literal string length, when
used in a test. This may be a silly question, but: how does this
happen?

The literal strings and the array variables have array types of fixed length. For example, "hello" has the type char[6]. The compiler will instantiate the SetArgumentPointee() function template with it. From that point on the type is fixed and will be used to explicitly instantiate SetArgumentPointeeAction. You just need to provide the appropriate template specialization for SetArgumentPointeeAction. This is also why you have to provide separate specializations for literal strings/arrays and for char* pointers -- char* and char[N] are different types. 

Zhanyong Wan (λx.x x)

unread,
Dec 1, 2010, 1:35:29 AM12/1/10
to Piotr Gorak, Google C++ Mocking Framework
On Sun, Nov 28, 2010 at 1:08 AM, Piotr Gorak <pgo...@gmail.com> wrote:
> In gmock-actions.h, I added:
>
> +template<size_t N>
> +class SetArgumentPointeeAction<N, const char*, false> {
> + public:
> +  explicit SetArgumentPointeeAction(const char* str) {
> +    str_ = new char[strlen(str)];
> +    strncpy(str_, str, strlen(str));
> +
> +  }
> +    template <typename Result, typename ArgumentTuple>
> +    void Perform(const ArgumentTuple& args) const {
> +      CompileAssertTypesEqual<void, Result>();
> +      strncpy(::std::tr1::get<N>(args), str_, strlen(str_));

SetArgPointee<k>(x) is supposed to do:

*arg_k = x;

where arg_k is the k-th argument of the mock function.

Apparently this is *not* what you want, which is:

strcpy(arg_k, x);

This is a different semantics, and thus should be handled by a
different construct. Instead of trying to abuse SetArgPointee<k>(x)
to do this, you should define a separate action for it.

--
Zhanyong

Piotr Gorak

unread,
Dec 1, 2010, 3:45:06 AM12/1/10
to Google C++ Mocking Framework
Zhanyong,

Hasn't this been the root cause of creating issue 61 in the first
place? The issue says that SetArgPointee<k> cannot take string
literals, like SetArgPointee<0>("hello") and that it should handle
this. Now what you wrote seems to be contrary the essence of the issue
(suggestion to define a different action). Am I missing something?

Piotr

>
> SetArgPointee<k>(x) is supposed to do:
>
>   *arg_k = x;
>
> where arg_k is the k-th argument of the mock function.
>
> Apparently this is *not* what you want, which is:
>
>   strcpy(arg_k, x);
>
> This is a different semantics, and thus should be handled by a
> different construct.  Instead of trying to abuse SetArgPointee<k>(x)
> to do this, you should define a separate action for it.
>
> Zhanyong

Zhanyong Wan (λx.x x)

unread,
Dec 1, 2010, 11:17:37 PM12/1/10
to Piotr Gorak, Google C++ Mocking Framework
On Wed, Dec 1, 2010 at 12:45 AM, Piotr Gorak <pgo...@gmail.com> wrote:
> Zhanyong,
>
> Hasn't this been the root cause of creating issue 61 in the first
> place?

No.

> The issue says that SetArgPointee<k> cannot take string
> literals, like SetArgPointee<0>("hello") and that it should handle
> this.

That's true. But it doesn't say that SetArgPointee<0>("hello") should
do a strcpy(). ;-)

> Now what you wrote seems to be contrary the essence of the issue
> (suggestion to define a different action). Am I missing something?

SetArgPointee<0>("hello") is supposed to do

*arg0 = "hello";

(for example, arg0 may be a string* or a const char**)

> Piotr
>
>>
>> SetArgPointee<k>(x) is supposed to do:
>>
>>   *arg_k = x;
>>
>> where arg_k is the k-th argument of the mock function.
>>
>> Apparently this is *not* what you want, which is:
>>
>>   strcpy(arg_k, x);
>>
>> This is a different semantics, and thus should be handled by a
>> different construct.  Instead of trying to abuse SetArgPointee<k>(x)
>> to do this, you should define a separate action for it.
>>
>> Zhanyong
>

--
Zhanyong

Reply all
Reply to author
Forward
0 new messages