I am getting the following error when trying to use
character in Type Is
gfortran -o build/lib/larsa.o -c -ffree-form -g -J./build/lib lib/larsa.f
lib/larsa.f:1558.14:
Type Is (Character)
1
Error: The type-spec at (1) shall specify that each length type parameter is assumed
lib/larsa.f:1490.14:
Type Is (Character)
1
Error: The type-spec at (1) shall specify that each length type parameter is assumed
This is the subroutine I have coded
Subroutine splits &
( &
s, delim, &
t1, t2, t3, t4, t5, t6, t7, t8, &
pos &
)
Character (len=*), Intent (in) :: s, delim
Character (len=*), Intent (in), Optional :: pos
Class (*), Intent (out) :: t1
Class (*), Intent (out), Optional :: t2, t3, t4, &
t5, t6, t7, t8
Integer :: nf
Select Type (t1)
Type Is (Character(len=*))
If (Present (pos)) Then
If (Present (t5)) Then
If (Present (t8)) Then
Call splits_str_to_str (s, delim, t1, t2, t3, t4, &
t5, t6, t7, t8, pos)
Else If (Present (t7)) Then
Call splits_str_to_str (s, delim, t1, t2, t3, t4, &
t5, t6, t7, pos)
Else If (Present (t6)) Then
Call splits_str_to_str (s, delim, t1, t2, t3, t4, &
t5, t6, pos)
Furthermore, I am using unlimited polymorphic entities on quite a few arguments.
Things then get complicated to have Select Type for all of them. The compiler
is giving me the error
gfortran -o build/lib/larsa.o -c -ffree-form -g -J./build/lib lib/larsa.f
lib/larsa.f:1569.51:
Call splits_str_to_str (s, delim, t1, t2, t3, t4, &
1
Error: Type mismatch in argument 's2' at (1); passed CLASS(*) to CHARACTER(1)
lib/larsa.f:1572.51:
Call splits_str_to_str (s, delim, t1, t2, t3, t4, &
1
Error: Type mismatch in argument 's2' at (1); passed CLASS(*) to CHARACTER(1)
lib/larsa.f:1575.51:
In addition to the syntax problem explained by IanH - you must use character(*) in select type - I will add something to your second problem. Possibly that should have been a new question.
You cannot assume that you know that two objects have the same dynamic type. You have to list all of them as selectors in select type otherwise the non-listed ones remain polymorphic. This is the very same reason why you use select type for t1.
I will also ads that it seems that your routine splits_str_to_str accepts optional arguments. In that case you don't have to test presents of all of them, you can pass them as optional actual arguments even if not present.
Unfortunately, to use them in select type as selectors you still need to test their presence.
One big design question remains. Why doesn't have splits character dummy arguments? Why it needs unlimited polymorphics?
This is just a consequence of the syntax rules and constraints for SELECT TYPE.
Conceptually the type selection is limited to the dynamic type of the object and any kind parameters for that type. The block to be executed is not chosen based on length type parameters, hence the syntax requires the specification of the length type parameter to be assumed.
So you simply want:
TYPE IS (CHARACTER(*))
Without the assumed specification, the implication of the syntax would be that the length type parameter of the selector had to match the default value of that parameter for the character type, i.e. 1.
Related
I have a function that extracts pointer to member type from other types, and this works:
template<class T2, class Array,
class Element = typename std::decay_t<Array>::element,
class PointerToMemberType = T2 Element::*
>
v f(Array&& a, PointerToMemberType pm);
However I cannot find the way to write PointerToMemberType = ???, without first definining Element, which should be able to be omitted.
How can I write PointerToMemberType directly without using the auxiliary Element?
I tried plain substitution but it doesn't work:
template<
class T2, class Array,
class PointerToMemberType = T2 typename std::decay_t<Array>::element::*
>
void f(Array&&, PointerToMemberType pm);
// error: expected identifier before ‘*’ token
677 | class PointerToMemberType = T2 typename std::decay_t<Array>::element::*
^
I also tried adding typename and parenthesis in several places.
Note that PointerToMemberType is not used for deduction at the moment, although I would try to use it in the future.
In some places it recommends using std::invoke so one doesn't need to deal with pointers-to-data-members, How would that fit or simplify something here?
A helper metafunction would do the trick quite nicely:
template <typename C, typename M>
using PM = M C::*;
template<
class T2, class Array,
class PointerToMemberType = PM<typename std::decay_t<Array>::element, T2>
>
...
As for whether it can be done "directly", the answer is yes, but you must omit the typename keyword. Your compiler should accept this:
template<
class T2, class Array,
class PointerToMemberType = T2 std::decay_t<Array>::element::*
>
...
According to the standard, [temp.res]/5:
A qualified name used as the name in a class-or-decltype (Clause 13) or an elaborated-type-specifier is
implicitly assumed to name a type, without the use of the typename keyword. In a nested-name-specifier
that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or
simple-template-id is implicitly assumed to name a type, without the use of the typename keyword. [ Note:
The typename keyword is not permitted by the syntax of these constructs. — end note ]
In our situation, we have the nested-name-specifier std::decay_t<Array>::element:: which immediately contains the nested-name-specifier std::decay_t<Array>:: which depends on a template parameter, so this paragraph tells us that typename is not necessary. Clearly, when std::decay_t<Array>::element is followed by ::, the compiler knows that std::decay_t<Array>::element is a type and not a data member nor a member function.
According to the note, the grammar forbids the unnecessary use of typename in this situation. The proper use of a typename-specifier according to [temp.res]/3 is:
typename nested-name-specifier identifier
Here, typename applies to the identifier after the entire nested-name-specifier has been applied, for example, in typename A::B::C::D, the :: operator binds more tightly than typename, so you are saying that typename A::B::C::D is a type. A nested-name-specifier cannot contain typename at the top level of one of its components, since from [expr.prim.id.qual], the leftmost component can only be a type-name, a namespace-name, or a decltype-specifier, and a type-name (unlike a type-id) can only be an unqualified name or a simple-template-id. Non-leftmost components can only be unqualified names or simple-template-ids (sometimes required to be prefixed by template).
The following code is legal in C++11.
template<int... N>
std::tuple<decltype(N)...> f()
{
return std::make_tuple(7 + N...);
}
What does it mean?
First of all, look at the template parameters: template <int ... N>. Even though a variable number of template arguments can be given to f, all of them must be of type int.
Now when you use f<t1, t2, ..., tn>, the parameter unpacking (7 + N...) will follow the pattern 7 + N and expand to
7 + t1, 7 + t2, 7 + t3, ..., 7 + tn
Therefore you end up with a tuple which contains each of your template arguments increased by seven. The details can be found in section 14.5.3 Variadic templates [temp.variadic].
3. A pack expansion consists of a pattern and an ellipsis, the instantiation of which produces zero or more instantiations of the pattern in a list [...].
I saw code like this earlier:
using A = std::vector<std::vector<T>...>
where T is a variadic list of template arguments. I wanted to know what the difference is between putting the parameter pack at the end of the last angle bracket and the first. For example:
using B = std::vector<std::vector<T...>>;
Both of these two compile fine but I don't know what the difference is.
Can someone explain? Thanks.
In a pack expansion the pattern that precedes the ... is repeated for each element of the pack, so vector<T>... means expand into vector<T1>, vector<T2>, vector<T3> whereas vector<T...> means expand into vector<T1, T2, T3>
If the parameter pack only has one element they're the same, but consider if the parameter pack has two elements, it should be obvious that
std::vector<std::vector<T1>, std::vector<T2>>
and
std::vector<std::vector<T1, T2>>
are not the same. The first one will not compile, the second template parameter for std::vector must be an allocator type, not a vector. The second one will compile if T2 is an allocator type.
When instantiating A<T1, T2, T3>, it is expanded to:
std::vector<std::vector<T1>, std::vector<T2>, std::vector<T3>>
Using the same template arguments to instantiate B, you get:
std::vector<std::vector<T1, T2, T3>>
In Andrei's talk on GoingNative 2012 he talks about Variadic Templates, and he explains at one point by way of the example underneath how the parameter pack expansions work. Being fairly new to this subject I found it fairly hard to follow how each case works, could anybody please explain how the expansion works in each function call of gun?
template<class... Ts> void fun(Ts... vs) {
gun(A<Ts...>::hun(vs)...);
gun(A<Ts...>::hun(vs...));
gun(A<Ts>::hun(vs)...);
}
1.
gun(A<Ts...>::hun(vs)...)
=> gun(A<T1, T2, …, Tn>::hun(vs)...)
=> gun(A<T1, T2, …, Tn>::hun(v1),
A<T1, T2, …, Tn>::hun(v2),
…,
A<T1, T2, …, Tn>::hun(vm))
2.
gun(A<Ts...>::hun(vs...))
=> gun(A<T1, T2, …, Tn>::hun(vs...))
=> gun(A<T1, T2, …, Tn>::hun(v1, v2, …, vm))
This should be obvious.
3.
gun(A<Ts>::hun(vs)...)
=> gun(A<T1>::hun(v1), A<T2>::hun(v2), …, A<Tn>::hun(vn))
(In this case the program won't compile if the lengths of Ts and vs differ)
The ... will expand a pattern (which includes any parameter packs) preceding it, meaning that, in foo(Ts, Us, Vs)..., each member of the list Ts, Us, Vs (enumerated in lock step) will be substituted into that pattern, and a comma separated list will be formed:
foo(Ts, Us, Vs)...
=> foo(T1, U1, V1), foo(T2, U2, V2), …, foo(Tn, Un, Vn)
And if there are nested expansions, the innermost patterns will be expanded first. Therefore, in case 1, the pattern Ts will first be expanded into T1, T2, …, Tn. And then, the pattern preceding the outer ... is A<T1, T2, …, Tn>::fun(vs) — note that Ts has been expanded — so it will be expanded to A<T1, T2, …, Tn>::fun(v1), A<T1, T2, …, Tn>::fun(v2), …, A<T1, T2, …, Tn>::fun(vm) by substituting v1, v2, etc. into vs.
KennyTM's answer is perfect. I just also like samples. But since his answer is abstract, I didn't feel like adding demos to his answer is the correct thing. So demos for his answer are here. I'm assuming his answer is right, I know nothing myself. (If you upvote this, upvote his too)
Obviously this is all psudocode just showing the expanded states.
void foo<void*,int,char,std::string>(nullptr, 32, '7', "BANANA") {
//gun(A<Ts...>::hun(vs)...);
gun(A<void*,int,char,std::string>::hun(nullptr)
,A<void*,int,char,std::string>::hun(32)
,A<void*,int,char,std::string>::hun('7')
,A<void*,int,char,std::string>::hun("BANANA")
);
//gun(A<Ts...>::hun(vs...));
gun(A<void*,int,char,std::string>::hun(nullptr, 32, '7', "BANANA");
//gun(A<Ts>::hun(vs)...);
gun(A<void*>::hun(nullptr)
,A<int>::hun(32),
,A<char>::hun('7'),
,A<std::string>::hun("BANANA")
);
}
The boost::function FAQ item 3 specifically addresses the scenario I am interested in:
Why are there workarounds for void
returns? C++ allows them! Void returns
are permitted by the C++ standard, as
in this code snippet:
void f();
void g() { return f(); }
This is a valid usage of
boost::function because void returns
are not used. With void returns, we
would attempting to compile ill-formed
code similar to:
int f();
void g() { return f(); }
In essence, not using void returns
allows boost::function to swallow a
return value. This is consistent with
allowing the user to assign and invoke
functions and function objects with
parameters that don't exactly match.
Unfortunately, this doesn't work in VS2008:
int Foo();
std::tr1::function<void()> Bar = Foo;
This produces errors starting with:
c:\Program Files\Microsoft Visual Studio 9.0\VC\include\xxcallfun(7) : error C2562: 'std::tr1::_Callable_fun<_Ty>::_ApplyX' : 'void' function returning a value
Is this a failing of the VS2008 TR1 implementation? Does this work in VS2010? Does TR1 address this capability? How about C++0x?
I believe tr1 addresses this issue. N1836 (the latest tr1 draft) says:
A function object f of type F is
Callable for argument types T1, T2,
..., TN and a return type R, if, given
lvalues t1, t2, ..., tNoftypesT1, T2,
..., TN,respectively,INVOKE(f, t1, t2,
..., tN)is well-formed([3.3]) and, if R
is not void, convertible to R.
In your example R is void, and so the last part of the requirements for Callable (convertible to R) is ignored.
However it looks like C++0x (C++11) changes the rules. In C++11 Callable is defined as INVOKE(f, t1, t2, ..., tN, R) which is defined in [func.require] as requiring INVOKE(f, t1, t2, ..., tN) to be implicitly convertible to R, with no exception for when R is void. So in C++11, your example should fail.